Completed
Push — develop ( 9193e7...62056c )
by Jaap
12:45 queued 02:43
created

MethodAssemblerTest::givenADocBlockObject()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 10
nc 2
nop 1
dl 0
loc 17
rs 9.4285
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
use phpDocumentor\Reflection\Fqsen;
21
use phpDocumentor\Reflection\Php\Argument;
22
use phpDocumentor\Reflection\Php\Method;
23
use phpDocumentor\Reflection\Php\Visibility;
24
use phpDocumentor\Reflection\Types\String_;
25
26
class MethodAssemblerTest extends \PHPUnit_Framework_TestCase
27
{
28
    /** @var MethodAssembler $fixture */
29
    protected $fixture;
30
31
    /** @var ArgumentAssembler|m\MockInterface */
32
    protected $argumentAssemblerMock;
33
34
    /** @var ProjectDescriptorBuilder|m\MockInterface */
35
    protected $builderMock;
36
37
    /**
38
     * Creates a new fixture to test with.
39
     */
40
    protected function setUp()
41
    {
42
        $this->builderMock = m::mock('phpDocumentor\Descriptor\ProjectDescriptorBuilder');
43
        $this->builderMock->shouldReceive('buildDescriptor')->andReturn(null);
44
45
        $this->argumentAssemblerMock = m::mock('phpDocumentor\Descriptor\Builder\Reflector\ArgumentAssembler');
46
        $this->argumentAssemblerMock->shouldReceive('getBuilder')->once()->andReturn(null);
47
        $this->argumentAssemblerMock->shouldReceive('setBuilder')->once();
48
49
        $this->fixture = new MethodAssembler($this->argumentAssemblerMock);
50
        $this->fixture->setBuilder($this->builderMock);
51
    }
52
53
    /**
54
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::__construct
55
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::create
56
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::mapReflectorToDescriptor
57
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArguments
58
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArgument
59
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addVariadicArgument
60
     */
61
    public function testCreateMethodDescriptorFromReflector()
62
    {
63
        // Arrange
64
        $namespace    = 'Namespace';
65
        $methodName   = 'goodbyeWorld';
66
        $argumentName = 'variableName';
67
68
        $argument = $this->givenAnArgumentWithName($argumentName);
69
        $methodReflectorMock = $this->givenAMethodReflector(
70
            $namespace,
71
            $methodName,
72
            $argument,
73
            $this->givenADocBlockObject(true)
74
        );
75
76
        $argumentDescriptor = new ArgumentDescriptor();
77
        $argumentDescriptor->setName($argumentName);
78
79
        $this->argumentAssemblerMock->shouldReceive('create')->andReturn($argumentDescriptor);
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...
80
81
        // Act
82
        $descriptor = $this->fixture->create($methodReflectorMock);
83
84
        // Assert
85
        $expectedFqsen = '\\' . $namespace . '::' . $methodName . '()';
86
        $this->assertSame($expectedFqsen, (string)$descriptor->getFullyQualifiedStructuralElementName());
87
        $this->assertSame($methodName, $descriptor->getName());
88
        $this->assertSame('\\' . $namespace, $descriptor->getNamespace());
89
        $this->assertSame('protected', (string)$descriptor->getVisibility());
90
        $this->assertSame(false, $descriptor->isFinal());
91
        $this->assertSame(false, $descriptor->isAbstract());
92
        $this->assertSame(false, $descriptor->isStatic());
93
94
        $argument = $descriptor->getArguments()->get($argumentName);
95
        $this->assertSame($argument->getName(), $argumentDescriptor->getName());
96
    }
97
98
    /**
99
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::__construct
100
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::create
101
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::mapReflectorToDescriptor
102
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArguments
103
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArgument
104
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addVariadicArgument
105
     */
106
    public function testCreateMethodDescriptorFromReflectorWhenDocblockIsAbsent()
107
    {
108
        // Arrange
109
        $namespace    = 'Namespace';
110
        $methodName   = 'goodbyeWorld';
111
        $argumentName = 'waveHand';
112
113
        $argumentDescriptorMock = $this->givenAnArgumentWithName($argumentName);
114
        $methodReflectorMock = $this->givenAMethodReflector(
115
            $namespace,
116
            $methodName,
117
            $argumentDescriptorMock
118
        );
119
120
        $argumentDescriptor = new ArgumentDescriptor();
121
        $argumentDescriptor->setName($argumentName);
122
123
        $this->argumentAssemblerMock->shouldReceive('create')->andReturn($argumentDescriptor);
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...
124
125
        // Act
126
        $descriptor = $this->fixture->create($methodReflectorMock);
127
128
        // Assert
129
        $argument = $descriptor->getArguments()->get($argumentName);
130
        $this->assertSame($argumentDescriptor, $argument);
131
    }
132
133
    /**
134
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::__construct
135
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::create
136
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::mapReflectorToDescriptor
137
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArguments
138
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addArgument
139
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\MethodAssembler::addVariadicArgument
140
     */
141
    public function testCreateMethodDescriptorFromReflectorWhenParamTagsAreAbsent()
142
    {
143
        // Arrange
144
        $namespace    = 'Namespace';
145
        $methodName   = 'goodbyeWorld';
146
        $argumentName = 'waveHand';
147
148
        $argumentDescriptorMock = $this->givenAnArgumentWithName($argumentName);
149
        $methodReflectorMock = $this->givenAMethodReflector(
150
            $namespace,
151
            $methodName,
152
            $argumentDescriptorMock,
153
            $this->givenADocBlockObject(false)
154
        );
155
156
        $argumentDescriptor = new ArgumentDescriptor();
157
        $argumentDescriptor->setName($argumentName);
158
159
        $this->argumentAssemblerMock->shouldReceive('create')->andReturn($argumentDescriptor);
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...
160
161
162
        // Act
163
        $descriptor = $this->fixture->create($methodReflectorMock);
164
165
        // Assert
166
        $argument = $descriptor->getArguments()->get($argumentName);
167
        $this->assertSame($argumentDescriptor, $argument);
168
    }
169
170
    /**
171
     * Creates a sample method reflector for the tests with the given data.
172
     *
173
     * @param string $namespace
174
     * @param string $methodName
175
     * @param Argument $argumentMock
176
     * @param DocBlock|m\MockInterface $docBlockMock
177
     * @return Method
178
     */
179
    protected function givenAMethodReflector($namespace, $methodName, Argument $argumentMock, $docBlockMock = null)
180
    {
181
        $method = new Method(
182
            new Fqsen('\\' . $namespace . '::' . $methodName . '()'),
183
            new Visibility(Visibility::PROTECTED_),
184
            $docBlockMock
0 ignored issues
show
Bug introduced by
It seems like $docBlockMock defined by parameter $docBlockMock on line 179 can also be of type object<Mockery\MockInterface>; however, phpDocumentor\Reflection\Php\Method::__construct() does only seem to accept null|object<phpDocumentor\Reflection\DocBlock>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
185
        );
186
187
        $method->addArgument($argumentMock);
188
189
        return $method;
190
    }
191
192
    /**
193
     * Generates a DocBlock object with applicable defaults for these tests.
194
     *
195
     * @return DocBlock|m\MockInterface
196
     */
197
    protected function givenADocBlockObject($withTags)
198
    {
199
        $docBlockDescription = new DocBlock\Description('This is an example description');
200
201
        $tags = [];
202
203
        if ($withTags) {
204
            $tags[] = new DocBlock\Tags\Param(
205
                'variableName',
206
                new String_(),
207
                true,
208
                new DocBlock\Description('foo')
209
            );
210
        }
211
212
        return new DocBlock('This is a example description', $docBlockDescription, $tags);
213
    }
214
215
    /**
216
     * Prepares a mock Argument with the given name.
217
     *
218
     * @param string $argumentName
219
     *
220
     * @return Argument
221
     */
222
    protected function givenAnArgumentWithName($argumentName)
223
    {
224
        return new Argument($argumentName);
225
    }
226
}
227