Passed
Push — master ( f1398a...71b321 )
by Dominik
02:02
created

MockByCallsTrait::getMockCallback()   A

Complexity

Conditions 5
Paths 1

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 24
ccs 10
cts 10
cp 1
rs 9.2248
c 0
b 0
f 0
cc 5
nc 1
nop 4
crap 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chubbyphp\Mock;
6
7
use Chubbyphp\Mock\Argument\ArgumentInterface;
8
use PHPUnit\Framework\MockObject\MockObject;
9
use PHPUnit\Framework\TestCase;
10
11
trait MockByCallsTrait
12
{
13
    /**
14
     * @param string $class
15
     * @param Call[] $calls
16
     *
17
     * @return MockObject
18
     */
19 8
    private function getMockByCalls(string $class, array $calls = []): MockObject
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
20
    {
21
        /** @var MockByCallsTrait|TestCase $this */
22 8
        $reflectionClass = new \ReflectionClass($class);
23
24 8
        $mockBuilder = $this->getMockBuilder($class)
0 ignored issues
show
Bug introduced by
It seems like getMockBuilder() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
25 8
            ->disableOriginalConstructor()
26 8
            ->disableOriginalClone();
27
28
        /* @var MockObject $mock */
29 8
        if ($reflectionClass->isAbstract() || $reflectionClass->isInterface()) {
30 7
            $mock = $mockBuilder->getMockForAbstractClass();
31
        } else {
32 1
            $mock = $mockBuilder->getMock();
33
        }
34
35 8
        $callCount = count($calls);
36
37 8
        if (0 === $callCount) {
38 2
            $mock->expects(self::never())->method(self::anything());
39
40 2
            return $mock;
41
        }
42
43 6
        foreach ($calls as $i => $call) {
44 6
            $mock->expects(self::at($i))
45 6
                ->method($call->getMethod())
46 6
                ->willReturnCallback($this->getMockCallback($class, $i, $call, $mock));
47
        }
48
49 6
        $callIndex = 0;
50
51 6
        $mock->expects(self::any())->method(self::anything())->willReturnCallback(
52 6
            function () use ($class, $callCount, &$callIndex) {
53 6
                if ($callIndex === $callCount) {
54 1
                    self::fail(sprintf('Additional call at index %d on class "%s"!', $callIndex, $class));
55
                }
56
57 6
                ++$callIndex;
58 6
            }
59
        );
60
61 6
        return $mock;
62
    }
63
64
    /**
65
     * @param string     $class
66
     * @param int        $i
67
     * @param Call       $call
68
     * @param MockObject $mock
69
     *
70
     * @return \Closure
71
     */
72
    private function getMockCallback(
73
        string $class,
74
        int $i,
75
        Call $call,
76
        MockObject $mock
77
    ): \Closure {
78 6
        return function () use ($class, $i, $call, $mock) {
79 6
            if ($call->hasWith()) {
80 6
                $this->compareArguments($class, $call->getMethod(), $i, $call->getWith(), func_get_args());
81
            }
82
83 5
            if (null !== $exception = $call->getException()) {
84 1
                throw $exception;
85
            }
86
87 4
            if ($call->hasReturnSelf()) {
88 1
                return $mock;
89
            }
90
91 3
            if ($call->hasReturn()) {
92 2
                return $call->getReturn();
93
            }
94 6
        };
95
    }
96
97
    /**
98
     * @param string $class
99
     * @param string $method
100
     * @param int    $i
101
     * @param array  $expectedArguments
102
     * @param array  $arguments
103
     */
104 6
    private function compareArguments(
105
        string $class,
106
        string $method,
107
        int $i,
108
        array $expectedArguments,
109
        array $arguments
110
    ) {
111 6
        $expectedArgumentsCount = count($expectedArguments);
112 6
        $argumentsCount = count($arguments);
113
114 6
        self::assertSame(
115 6
            $expectedArgumentsCount,
116 6
            $argumentsCount,
117 6
            sprintf(
118 6
                'Method "%s" on class "%s" at call with index %d, got %d arguments, but %d are expected',
119 6
                $method,
120 6
                $class,
121 6
                $i,
122 6
                $expectedArgumentsCount,
123 6
                $argumentsCount
124
            )
125
        );
126
127 6
        foreach ($expectedArguments as $y => $expectedArgument) {
128 6
            if ($expectedArgument instanceof ArgumentInterface) {
129 2
                $expectedArgument->assert(
130 2
                    $arguments[$y],
131 2
                    sprintf('Method "%s" on class "%s" at call with index %d', $method, $class, $i)
132
                );
133
134 1
                continue;
135
            }
136
137 5
            self::assertSame(
138 5
                $expectedArgument,
139 5
                $arguments[$y],
140 5
                sprintf('Method "%s" on class "%s" at call with index %d', $method, $class, $i)
141
            );
142
        }
143 5
    }
144
}
145