1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
|
4
|
|
|
namespace Moka\Tests; |
5
|
|
|
|
6
|
|
|
use Moka\Exception\InvalidArgumentException; |
7
|
|
|
use Moka\Factory\StubFactory; |
8
|
|
|
use Moka\Strategy\MockingStrategyInterface; |
9
|
|
|
use Moka\Stub\StubSet; |
10
|
|
|
use PHPUnit\Framework\TestCase; |
11
|
|
|
use Tests\AbstractTestClass; |
12
|
|
|
use Tests\BarTestClass; |
13
|
|
|
use Tests\FooTestClass; |
14
|
|
|
use Tests\TestInterface; |
15
|
|
|
|
16
|
|
|
abstract class MokaMockingStrategyTestCase extends TestCase |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* @var MockingStrategyInterface |
20
|
|
|
*/ |
21
|
|
|
protected $strategy; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var object |
25
|
|
|
*/ |
26
|
|
|
protected $mock; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var StubSet |
30
|
|
|
*/ |
31
|
|
|
protected $stubs; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var string |
35
|
|
|
*/ |
36
|
|
|
private $className; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var array |
40
|
|
|
*/ |
41
|
|
|
private $methodsWithValues = []; |
42
|
|
|
|
43
|
|
|
private $methodName; |
44
|
|
|
|
45
|
|
|
final public function testGetMockTypeSuccess() |
46
|
|
|
{ |
47
|
|
|
$this->assertInternalType('string', $this->strategy->getMockType()); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
final public function testBuildClassSuccess() |
51
|
|
|
{ |
52
|
|
|
$this->assertInstanceOf($this->strategy->getMockType(), $this->mock); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
final public function testBuildInterfaceSuccess() |
56
|
|
|
{ |
57
|
|
|
$mock = $this->strategy->build(TestInterface::class); |
58
|
|
|
|
59
|
|
|
$this->assertInstanceOf($this->strategy->getMockType(), $mock); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
final public function testBuildAbstractClassSuccess() |
63
|
|
|
{ |
64
|
|
|
$mock = $this->strategy->build(AbstractTestClass::class); |
65
|
|
|
|
66
|
|
|
$this->assertInstanceOf($this->strategy->getMockType(), $mock); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
final public function testDecorateFailure() |
70
|
|
|
{ |
71
|
|
|
$this->expectException(InvalidArgumentException::class); |
72
|
|
|
|
73
|
|
|
$this->strategy->decorate(new \stdClass(), $this->stubs); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
final public function testDecorateWrongTypeHintFailure() |
77
|
|
|
{ |
78
|
|
|
$this->strategy->decorate($this->mock, StubFactory::fromArray([ |
|
|
|
|
79
|
|
|
'getSelf' => mt_rand() |
80
|
|
|
])); |
81
|
|
|
|
82
|
|
|
$this->expectException(\TypeError::class); |
83
|
|
|
$this->strategy->get($this->mock)->getSelf(); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
final public function testDecorateSingleCallSuccess() |
87
|
|
|
{ |
88
|
|
|
$this->assertSame($this->methodsWithValues['isTrue'], $this->strategy->get($this->mock)->isTrue()); |
89
|
|
|
|
90
|
|
|
$this->expectException(\Exception::class); |
91
|
|
|
$this->expectExceptionMessage($this->methodsWithValues['throwException']->getMessage()); |
92
|
|
|
$this->strategy->get($this->mock)->throwException(); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
final public function testDecorateMultipleCallsSuccess() |
96
|
|
|
{ |
97
|
|
|
$this->assertSame($this->methodsWithValues['getInt'], $this->strategy->get($this->mock)->getInt()); |
98
|
|
|
$this->assertSame($this->methodsWithValues['getInt'], $this->strategy->get($this->mock)->getInt()); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
final public function testDecorateOverriddenCallsFailure() |
102
|
|
|
{ |
103
|
|
|
$this->strategy->decorate($this->mock, StubFactory::fromArray([ |
|
|
|
|
104
|
|
|
'getInt' => mt_rand(), |
105
|
|
|
'throwException' => mt_rand() |
106
|
|
|
])); |
107
|
|
|
|
108
|
|
|
$this->assertSame($this->methodsWithValues['getInt'], $this->strategy->get($this->mock)->getInt()); |
109
|
|
|
$this->assertSame($this->methodsWithValues['getInt'], $this->strategy->get($this->mock)->getInt()); |
110
|
|
|
|
111
|
|
|
$this->expectException(\Exception::class); |
112
|
|
|
$this->expectExceptionMessage($this->methodsWithValues['throwException']->getMessage()); |
113
|
|
|
$this->strategy->get($this->mock)->throwException(); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
final public function testDecorateCallWithArgumentSuccess() |
117
|
|
|
{ |
118
|
|
|
$this->assertSame($this->methodsWithValues['withArgument'], $this->strategy->get($this->mock)->withArgument(mt_rand())); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
final public function testDecorateCallWithMissingArgumentFailure() |
122
|
|
|
{ |
123
|
|
|
$this->expectException(\Error::class); |
124
|
|
|
|
125
|
|
|
$this->strategy->get($this->mock)->withArgument(); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
final public function testDecorateCallWithWrongArgumentFailure() |
129
|
|
|
{ |
130
|
|
|
$this->expectException(\TypeError::class); |
131
|
|
|
|
132
|
|
|
$this->strategy->get($this->mock)->withArgument('string'); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
final public function testGetSuccess() |
136
|
|
|
{ |
137
|
|
|
$this->assertInstanceOf($this->className, $this->strategy->get($this->mock)); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
final public function testGetFailure() |
141
|
|
|
{ |
142
|
|
|
$this->expectException(InvalidArgumentException::class); |
143
|
|
|
|
144
|
|
|
$this->strategy->get(new \stdClass()); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
public function testMethodForward() |
148
|
|
|
{ |
149
|
|
|
if (!$this->methodName) { |
150
|
|
|
return; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
try { |
154
|
|
|
$this->strategy->build(\stdClass::class)->{$this->methodName}(); |
155
|
|
|
$this->assertTrue(true); |
156
|
|
|
} catch (\Error $e) { |
157
|
|
|
$this->assertEquals(0, preg_match('/^Call to undefined method/', $e->getMessage())); |
158
|
|
|
} |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
protected function setUp() |
162
|
|
|
{ |
163
|
|
|
$this->className = [ |
164
|
|
|
FooTestClass::class, |
165
|
|
|
BarTestClass::class |
166
|
|
|
][random_int(0, 1)]; |
167
|
|
|
|
168
|
|
|
$this->methodsWithValues = [ |
169
|
|
|
'isTrue' => !!random_int(0, 1), |
170
|
|
|
'getInt' => mt_rand(), |
171
|
|
|
'withArgument' => mt_rand(), |
172
|
|
|
'throwException' => new \Exception('' . mt_rand()) |
173
|
|
|
]; |
174
|
|
|
|
175
|
|
|
$this->mock = $this->strategy->build($this->className); |
176
|
|
|
|
177
|
|
|
// Mocking a StubSet is way too difficult. |
178
|
|
|
$this->stubs = StubFactory::fromArray($this->methodsWithValues); |
|
|
|
|
179
|
|
|
|
180
|
|
|
$this->strategy->decorate($this->mock, $this->stubs); |
|
|
|
|
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
final protected function setStrategy(MockingStrategyInterface $strategy) |
184
|
|
|
{ |
185
|
|
|
$this->strategy = $strategy; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
final protected function setMethodName(string $methodName) |
189
|
|
|
{ |
190
|
|
|
$this->methodName = $methodName; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
final protected function getRandomFQCN() |
194
|
|
|
{ |
195
|
|
|
return 'foo_' . rand(); |
196
|
|
|
} |
197
|
|
|
} |
198
|
|
|
|
This check looks at variables that 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.