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

MockByCallsTrait   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 134
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 14
lcom 0
cbo 2
dl 0
loc 134
ccs 58
cts 58
cp 1
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A compareArguments() 0 40 3
B getMockByCalls() 0 44 6
A getMockCallback() 0 24 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