FunctionAssemblerTest   A
last analyzed

Complexity

Total Complexity 7

Size/Duplication

Total Lines 125
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 0
Metric Value
dl 0
loc 125
rs 10
c 0
b 0
f 0
wmc 7
lcom 1
cbo 14

5 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 19 3
A testCreateFunctionDescriptorFromReflector() 0 31 1
A givenAFunctionReflector() 0 11 1
A givenADocBlockObject() 0 11 1
A givenAnArgumentWithName() 0 4 1
1
<?php
2
/**
3
 * phpDocumentor
4
 *
5
 * PHP Version 5.3
6
 *
7
 * @author    Mike van Riel <[email protected]>
8
 * @author    Sven Hagemann <[email protected]>
9
 * @copyright 2010-2018 Mike van Riel / Naenius (http://www.naenius.com)
10
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
11
 * @link      http://phpdoc.org
12
 */
13
14
namespace phpDocumentor\Descriptor\Builder\Reflector;
15
16
use Mockery as m;
17
use phpDocumentor\Descriptor\ArgumentDescriptor;
18
use phpDocumentor\Descriptor\PackageDescriptor;
19
use phpDocumentor\Descriptor\ProjectDescriptorBuilder;
20
use phpDocumentor\Reflection\DocBlock;
21
use phpDocumentor\Reflection\Fqsen;
22
use phpDocumentor\Reflection\Php\Argument;
23
use phpDocumentor\Reflection\Php\Function_;
24
use phpDocumentor\Reflection\Types\Mixed_;
25
26
class FunctionAssemblerTest extends \Mockery\Adapter\Phpunit\MockeryTestCase
27
{
28
    /** @var FunctionAssembler $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')->andReturnUsing(
44
            function ($value) {
45
                switch (get_class($value)) {
46
                    case DocBlock\Tags\Generic::class && $value->getName() === 'package':
47
                        return new PackageDescriptor();
48
                    default:
49
                        throw new \InvalidArgumentException('didn\'t expect ' . get_class($value));
50
                }
51
            }
52
        );
53
        $this->argumentAssemblerMock = m::mock('phpDocumentor\Descriptor\Builder\Reflector\ArgumentAssembler');
54
        $this->argumentAssemblerMock->shouldReceive('getBuilder')->andReturnNull();
55
        $this->argumentAssemblerMock->shouldReceive('setBuilder')->with($this->builderMock);
56
        $this->fixture = new FunctionAssembler($this->argumentAssemblerMock);
57
        $this->fixture->setBuilder($this->builderMock);
58
    }
59
60
    /**
61
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\FunctionAssembler::__construct
62
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\FunctionAssembler::create
63
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\FunctionAssembler::mapReflectorPropertiesOntoDescriptor
64
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\FunctionAssembler::addArgumentsToFunctionDescriptor
65
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\FunctionAssembler::createArgumentDescriptor
66
     * @covers \phpDocumentor\Descriptor\Builder\Reflector\FunctionAssembler::addArgumentDescriptorToFunction
67
     */
68
    public function testCreateFunctionDescriptorFromReflector()
69
    {
70
        // Arrange
71
        $namespace = 'Namespace';
72
        $functionName = 'goodbyeWorld';
73
        $argumentName = 'waveHand';
74
75
        $argument = $this->givenAnArgumentWithName($argumentName);
76
        $functionReflectorMock = $this->givenAFunctionReflector(
77
            $namespace,
78
            $functionName,
79
            $argument,
80
            $this->givenADocBlockObject()
81
        );
82
        $argumentDescriptor = new ArgumentDescriptor();
83
        $argumentDescriptor->setName($argumentName);
84
85
        $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...
86
87
        // Act
88
        $descriptor = $this->fixture->create($functionReflectorMock);
89
90
        // Assert
91
        $expectedFqsen = '\\' . $namespace . '\\' . $functionName . '()';
92
        $this->assertSame($expectedFqsen, (string) $descriptor->getFullyQualifiedStructuralElementName());
93
        $this->assertSame($functionName, $descriptor->getName());
94
        $this->assertSame('\\' . $namespace, $descriptor->getNamespace());
95
96
        $argument = $descriptor->getArguments()->get($argumentName);
97
        $this->assertSame($argumentDescriptor, $argument);
98
    }
99
100
    /**
101
     * Creates a sample function reflector for the tests with the given data.
102
     *
103
     * @param string $namespace
104
     * @param string $functionName
105
     * @param Argument $argumentMock
106
     * @param DocBlock|m\MockInterface $docBlockMock
107
     *
108
     * @return Function_
109
     */
110
    protected function givenAFunctionReflector($namespace, $functionName, $argumentMock, $docBlockMock)
111
    {
112
        $functionReflectorMock = new Function_(
113
            new Fqsen('\\' . $namespace . '\\' . $functionName . '()'),
114
            $docBlockMock
0 ignored issues
show
Bug introduced by
It seems like $docBlockMock defined by parameter $docBlockMock on line 110 can also be of type object<Mockery\MockInterface>; however, phpDocumentor\Reflection...unction_::__construct() does only seem to accept object<phpDocumentor\Reflection\DocBlock>|null, 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...
115
        );
116
117
        $functionReflectorMock->addArgument($argumentMock);
118
119
        return $functionReflectorMock;
120
    }
121
122
    /**
123
     * Generates a DocBlock object with applicable defaults for these tests.
124
     *
125
     * @return DocBlock
126
     */
127
    protected function givenADocBlockObject()
128
    {
129
        $docBlockDescription = new DocBlock\Description('This is an example description');
130
        return new DocBlock(
131
            'This is a example description',
132
            $docBlockDescription,
133
            [
134
                new DocBlock\Tags\Generic('package', new DocBlock\Description('PackageName')),
135
            ]
136
        );
137
    }
138
139
    /**
140
     * Prepares a mock Argument with the given name.
141
     *
142
     * @param string $argumentName
143
     *
144
     * @return Argument
145
     */
146
    protected function givenAnArgumentWithName($argumentName)
147
    {
148
        return new Argument($argumentName);
149
    }
150
}
151