Completed
Push — develop ( 8b2e58...a5af85 )
by Jaap
09:43
created

MethodAssemblerTest::givenADocBlockObject()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 17
nc 2
nop 1
dl 0
loc 26
rs 8.8571
c 1
b 0
f 0
1
<?php
2
/**
3
 * This file is part of phpDocumentor.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @copyright 2010-2017 Mike van Riel<[email protected]>
9
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
10
 * @link      http://phpdoc.org
11
 */
12
13
namespace phpDocumentor\Descriptor\Builder\Reflector;
14
15
use phpDocumentor\Descriptor\ArgumentDescriptor;
16
use phpDocumentor\Descriptor\ProjectDescriptorBuilder;
17
use phpDocumentor\Reflection\DocBlock;
18
use phpDocumentor\Reflection\ClassReflector\MethodReflector;
19
use Mockery as m;
20
21
class MethodAssemblerTest extends \PHPUnit_Framework_TestCase
22
{
23
    /** @var MethodAssembler $fixture */
24
    protected $fixture;
25
26
    /** @var ArgumentAssembler|m\MockInterface */
27
    protected $argumentAssemblerMock;
28
29
    /** @var ProjectDescriptorBuilder|m\MockInterface */
30
    protected $builderMock;
31
32
    /**
33
     * Creates a new fixture to test with.
34
     */
35
    protected function setUp()
36
    {
37
        $this->builderMock = m::mock('phpDocumentor\Descriptor\ProjectDescriptorBuilder');
38
        $this->builderMock->shouldReceive('buildDescriptor')->andReturn(null);
39
40
        $this->argumentAssemblerMock = m::mock('phpDocumentor\Descriptor\Builder\Reflector\ArgumentAssembler');
41
        $this->argumentAssemblerMock->shouldReceive('getBuilder')->once()->andReturn(null);
42
        $this->argumentAssemblerMock->shouldReceive('setBuilder')->once();
43
44
        $this->fixture = new MethodAssembler($this->argumentAssemblerMock);
45
        $this->fixture->setBuilder($this->builderMock);
46
    }
47
48
    /**
49
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::__construct
50
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::create
51
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::mapReflectorToDescriptor
52
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArguments
53
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArgument
54
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addVariadicArgument
55
     */
56
    public function testCreateMethodDescriptorFromReflector()
57
    {
58
        // Arrange
59
        $namespace    = 'Namespace';
60
        $methodName   = 'goodbyeWorld';
61
        $argumentName = 'variableName';
62
63
        $argumentDescriptorMock = $this->givenAnArgumentWithName($argumentName);
64
        $methodReflectorMock = $this->givenAMethodReflector(
65
            $namespace,
66
            $methodName,
67
            $argumentDescriptorMock,
68
            $this->givenADocBlockObject(true)
69
        );
70
71
        // Act
72
        $descriptor = $this->fixture->create($methodReflectorMock);
0 ignored issues
show
Bug introduced by
It seems like $methodReflectorMock defined by $this->givenAMethodRefle...nADocBlockObject(true)) on line 64 can also be of type object<Mockery\MockInterface>; however, phpDocumentor\Descriptor...thodAssembler::create() does only seem to accept object<phpDocumentor\Ref...lector\MethodReflector>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
73
74
        // Assert
75
        $expectedFqsen = $namespace . '\\' . $methodName . '()';
76
        $this->assertSame($expectedFqsen, $descriptor->getFullyQualifiedStructuralElementName());
77
        $this->assertSame($methodName, $descriptor->getName());
78
        $this->assertSame('\\' . $namespace, $descriptor->getNamespace());
79
        $this->assertSame('protected', $descriptor->getVisibility());
80
        $this->assertSame(false, $descriptor->isFinal());
81
        $this->assertSame(false, $descriptor->isAbstract());
82
        $this->assertSame(false, $descriptor->isStatic());
83
84
        $argument = $descriptor->getArguments()->get($argumentName);
85
        $this->assertSame($argument->getName(), $argumentDescriptorMock->getName());
86
    }
87
88
    /**
89
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::__construct
90
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::create
91
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::mapReflectorToDescriptor
92
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArguments
93
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArgument
94
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addVariadicArgument
95
     */
96
    public function testCreateMethodDescriptorFromReflectorWhenDocblockIsAbsent()
97
    {
98
        // Arrange
99
        $namespace    = 'Namespace';
100
        $methodName   = 'goodbyeWorld';
101
        $argumentName = 'waveHand';
102
103
        $argumentDescriptorMock = $this->givenAnArgumentWithName($argumentName);
104
        $methodReflectorMock = $this->givenAMethodReflector(
105
            $namespace,
106
            $methodName,
107
            $argumentDescriptorMock
108
        );
109
110
        // Act
111
        $descriptor = $this->fixture->create($methodReflectorMock);
0 ignored issues
show
Bug introduced by
It seems like $methodReflectorMock defined by $this->givenAMethodRefle...argumentDescriptorMock) on line 104 can also be of type object<Mockery\MockInterface>; however, phpDocumentor\Descriptor...thodAssembler::create() does only seem to accept object<phpDocumentor\Ref...lector\MethodReflector>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
112
113
        // Assert
114
        $argument = $descriptor->getArguments()->get($argumentName);
115
        $this->assertSame($argument, $argumentDescriptorMock);
116
    }
117
118
    /**
119
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::__construct
120
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::create
121
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::mapReflectorToDescriptor
122
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArguments
123
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArgument
124
     * @covers phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addVariadicArgument
125
     */
126
    public function testCreateMethodDescriptorFromReflectorWhenParamTagsAreAbsent()
127
    {
128
        // Arrange
129
        $namespace    = 'Namespace';
130
        $methodName   = 'goodbyeWorld';
131
        $argumentName = 'waveHand';
132
133
        $argumentDescriptorMock = $this->givenAnArgumentWithName($argumentName);
134
        $methodReflectorMock = $this->givenAMethodReflector(
135
            $namespace,
136
            $methodName,
137
            $argumentDescriptorMock,
138
            $this->givenADocBlockObject(false)
139
        );
140
141
        // Act
142
        $descriptor = $this->fixture->create($methodReflectorMock);
0 ignored issues
show
Bug introduced by
It seems like $methodReflectorMock defined by $this->givenAMethodRefle...ADocBlockObject(false)) on line 134 can also be of type object<Mockery\MockInterface>; however, phpDocumentor\Descriptor...thodAssembler::create() does only seem to accept object<phpDocumentor\Ref...lector\MethodReflector>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
143
144
        // Assert
145
        $argument = $descriptor->getArguments()->get($argumentName);
146
        $this->assertSame($argument, $argumentDescriptorMock);
147
    }
148
149
    /**
150
     * Creates a sample method reflector for the tests with the given data.
151
     *
152
     * @param string                             $namespace
153
     * @param string                             $methodName
154
     * @param ArgumentDescriptor|m\MockInterface $argumentMock
155
     * @param DocBlock|m\MockInterface           $docBlockMock
156
     *
157
     * @return MethodReflector|m\MockInterface
158
     */
159
    protected function givenAMethodReflector($namespace, $methodName, $argumentMock, $docBlockMock = null)
160
    {
161
        $methodReflectorMock = m::mock('phpDocumentor\Reflection\MethodReflector');
162
        $methodReflectorMock->shouldReceive('getName')->andReturn($namespace . '\\' . $methodName);
163
        $methodReflectorMock->shouldReceive('getShortName')->andReturn($methodName);
164
        $methodReflectorMock->shouldReceive('getNamespace')->andReturn($namespace);
165
        $methodReflectorMock->shouldReceive('getDocBlock')->andReturn($docBlockMock);
166
        $methodReflectorMock->shouldReceive('getLinenumber')->andReturn(128);
167
        $methodReflectorMock->shouldReceive('getArguments')->andReturn(array($argumentMock));
168
        $methodReflectorMock->shouldReceive('getVisibility')->andReturn('protected');
169
        $methodReflectorMock->shouldReceive('isFinal')->andReturn(false);
170
        $methodReflectorMock->shouldReceive('isAbstract')->andReturn(false);
171
        $methodReflectorMock->shouldReceive('isStatic')->andReturn(false);
172
173
        return $methodReflectorMock;
174
    }
175
176
    /**
177
     * Generates a DocBlock object with applicable defaults for these tests.
178
     *
179
     * @return DocBlock|m\MockInterface
180
     */
181
    protected function givenADocBlockObject($withTags)
182
    {
183
        $docBlockDescription = new DocBlock\Description('This is an example description');
184
185
        $docBlockMock = m::mock('phpDocumentor\Reflection\DocBlock');
186
        $docBlockMock->shouldReceive('getTags')->andReturn(array());
187
        $docBlockMock->shouldReceive('getShortDescription')->andReturn('This is a example description');
188
        $docBlockMock->shouldReceive('getLongDescription')->andReturn($docBlockDescription);
189
190
        if ($withTags) {
191
            $docBlockMock->shouldReceive('getTagsByName')->andReturnUsing(function ($param) {
0 ignored issues
show
Unused Code introduced by
The parameter $param is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
192
                $tag = m::mock('phpDocumentor\Reflection\DocBlock\Tag');
193
194
                $tag->shouldReceive('isVariadic')->once()->andReturn(true);
195
                $tag->shouldReceive('getVariableName')->andReturn('variableName');
196
                $tag->shouldReceive('getTypes')->andReturn(array());
197
                $tag->shouldReceive('getDescription');
198
199
                return array($tag);
200
            });
201
        } else {
202
            $docBlockMock->shouldReceive('getTagsByName')->andReturn(array());
203
        }
204
205
        return $docBlockMock;
206
    }
207
208
    /**
209
     * Prepares a mock Argument with the given name.
210
     *
211
     * @param string $argumentName
212
     *
213
     * @return ArgumentDescriptor|m\MockInterface
214
     */
215
    protected function givenAnArgumentWithName($argumentName)
216
    {
217
        $argumentMock = m::mock('phpDocumentor\Descriptor\ArgumentDescriptor');
218
        $argumentMock->shouldReceive('getName')->andReturn($argumentName);
219
        $argumentMock->shouldReceive('setMethod');
220
        $this->argumentAssemblerMock->shouldReceive('create')->andReturn($argumentMock);
0 ignored issues
show
Bug introduced by
The method shouldReceive does only exist in Mockery\MockInterface, but not in phpDocumentor\Descriptor...ector\ArgumentAssembler.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
221
222
        return $argumentMock;
223
    }
224
}
225