This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | declare(strict_types=1); |
||
3 | |||
4 | namespace Moka\Tests; |
||
5 | |||
6 | use Moka\Exception\InvalidArgumentException; |
||
7 | use Moka\Exception\MockNotCreatedException; |
||
8 | use Moka\Strategy\MockingStrategyInterface; |
||
9 | use PHPUnit\Framework\TestCase; |
||
10 | |||
11 | abstract class MokaMockingStrategyTestCase extends TestCase |
||
12 | { |
||
13 | private const FQCN_EMPTY = ''; |
||
14 | private const FQCN_INVALID = 'Foo Bar'; |
||
15 | private const FQCN_NONEXISTENT_TEMPLATE = 'Foo_%d'; |
||
16 | |||
17 | /** |
||
18 | * @var MockingStrategyInterface |
||
19 | */ |
||
20 | protected $strategy; |
||
21 | |||
22 | /** |
||
23 | * @var object |
||
24 | */ |
||
25 | protected $mock; |
||
26 | |||
27 | /** |
||
28 | * @var array |
||
29 | */ |
||
30 | protected $namesWithValues; |
||
31 | |||
32 | /** |
||
33 | * @return void |
||
34 | */ |
||
35 | protected function setUp(): void |
||
36 | { |
||
37 | $this->namesWithValues = [ |
||
38 | '$property' => mt_rand(), |
||
39 | '$public' => mt_rand(), |
||
40 | '$private' => mt_rand(), |
||
41 | 'isTrue' => (bool)random_int(0, 1), |
||
42 | 'getInt' => mt_rand(), |
||
43 | 'getReference' => [], |
||
44 | 'withArgument' => mt_rand(), |
||
45 | 'throwException' => new \Exception((string)mt_rand()) |
||
46 | ]; |
||
47 | |||
48 | $this->mock = $this->strategy->build($this->getRandomFQCN()); |
||
49 | $this->strategy->decorate($this->mock, $this->namesWithValues); |
||
50 | } |
||
51 | |||
52 | /** |
||
53 | * @throws \Exception |
||
54 | * @throws \Moka\Exception\NotImplementedException |
||
55 | */ |
||
56 | final public function testGetMockTypeSuccess() |
||
57 | { |
||
58 | $this->assertInternalType('string', $this->strategy->getMockType()); |
||
0 ignored issues
–
show
|
|||
59 | } |
||
60 | |||
61 | /** |
||
62 | * @dataProvider fqcnProvider |
||
63 | * @param bool $required |
||
64 | * @param string $fqcnType |
||
65 | * @param string[] $fqcns |
||
66 | * @throws \Exception |
||
67 | * @throws \Moka\Exception\NotImplementedException |
||
68 | */ |
||
69 | final public function testBuildAndGet(bool $required, string $fqcnType, string ...$fqcns) |
||
70 | { |
||
71 | if (true === $required) { |
||
72 | $this->buildAndGet(...$fqcns); |
||
73 | } else { |
||
74 | $this->tryBuildAndGet($fqcnType, ...$fqcns); |
||
75 | } |
||
76 | } |
||
77 | |||
78 | /** |
||
79 | * @requires PHP 7.1 |
||
80 | */ |
||
81 | public function testBuildWithPHP71Class() |
||
82 | { |
||
83 | $this->checkMock( |
||
84 | $this->strategy->build(PHP71TestClass::class) |
||
85 | ); |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * @return void |
||
90 | */ |
||
91 | final public function testDecorateFakeMockFailure() |
||
92 | { |
||
93 | $this->expectException(InvalidArgumentException::class); |
||
94 | |||
95 | $this->strategy->decorate(new FooTestClass(), $this->namesWithValues); |
||
96 | } |
||
97 | |||
98 | /** |
||
99 | * @throws \Exception |
||
100 | */ |
||
101 | final public function testDecorateWithPropertySuccess() |
||
102 | { |
||
103 | $this->assertEquals( |
||
104 | $this->namesWithValues['$property'], |
||
105 | $this->strategy->get($this->mock)->property |
||
106 | ); |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * @throws \Exception |
||
111 | */ |
||
112 | final public function testDecorateWithPublicPropertySuccess() |
||
113 | { |
||
114 | $this->assertEquals( |
||
115 | $this->namesWithValues['$public'], |
||
116 | $this->strategy->get($this->mock)->public |
||
117 | ); |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * @return void |
||
122 | */ |
||
123 | final public function testDecorateWithProtectedPropertyFailure() |
||
124 | { |
||
125 | $this->expectException(\Error::class); |
||
126 | |||
127 | $this->strategy->decorate($this->mock, [ |
||
128 | '$protected' => mt_rand() |
||
129 | ]); |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * @throws \Exception |
||
134 | */ |
||
135 | final public function testDecorateWithPrivatePropertySuccess() |
||
136 | { |
||
137 | $this->assertEquals( |
||
138 | $this->namesWithValues['$private'], |
||
139 | $this->strategy->get($this->mock)->private |
||
140 | ); |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * @return void |
||
145 | */ |
||
146 | final public function testDecorateWithWrongTypeHintFailure() |
||
147 | { |
||
148 | $this->strategy->decorate($this->mock, [ |
||
149 | 'getSelf' => mt_rand() |
||
150 | ]); |
||
151 | |||
152 | $this->expectException(\TypeError::class); |
||
153 | $this->strategy->get($this->mock)->getSelf(); |
||
154 | } |
||
155 | |||
156 | /** |
||
157 | * @return void |
||
158 | */ |
||
159 | final public function testDecorateWithNonexistentMethod() |
||
160 | { |
||
161 | try { |
||
162 | $value = mt_rand(); |
||
163 | $this->strategy->decorate($this->mock, [ |
||
164 | 'nonexistentMethod' => $value |
||
165 | ]); |
||
166 | |||
167 | $this->assertSame( |
||
168 | $value, |
||
169 | $this->strategy->get($this->mock)->nonexistentMethod() |
||
170 | ); |
||
171 | } catch (\Throwable $e) { |
||
172 | $this->markFeatureUnsupported('stubbing a nonexistent method'); |
||
173 | } |
||
174 | } |
||
175 | |||
176 | /** |
||
177 | * @return void |
||
178 | */ |
||
179 | final public function testCallUnstubbedMethod() |
||
180 | { |
||
181 | try { |
||
182 | $mock = $this->strategy->build(FooTestClass::class); |
||
183 | |||
184 | $this->assertInstanceOf( |
||
185 | TestInterface::class, |
||
186 | $this->strategy->get($mock)->getSelf() |
||
187 | ); |
||
188 | } catch (\Throwable $e) { |
||
189 | $this->markFeatureUnsupported('calling an unstubbed method'); |
||
190 | } |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * @throws \Exception |
||
195 | */ |
||
196 | final public function testSingleMethodCallSuccess() |
||
197 | { |
||
198 | $this->assertSame( |
||
199 | $this->namesWithValues['isTrue'], |
||
200 | $this->strategy->get($this->mock)->isTrue() |
||
201 | ); |
||
202 | |||
203 | $this->expectException(\Exception::class); |
||
204 | $this->expectExceptionMessage($this->namesWithValues['throwException']->getMessage()); |
||
205 | $this->strategy->get($this->mock)->throwException(); |
||
206 | } |
||
207 | |||
208 | /** |
||
209 | * @throws \Exception |
||
210 | */ |
||
211 | final public function testMultipleMethodCallsSuccess() |
||
212 | { |
||
213 | $this->assertSame( |
||
214 | $this->namesWithValues['getInt'], |
||
215 | $this->strategy->get($this->mock)->getInt() |
||
216 | ); |
||
217 | |||
218 | $this->assertSame( |
||
219 | $this->namesWithValues['getInt'], |
||
220 | $this->strategy->get($this->mock)->getInt() |
||
221 | ); |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * @throws \Exception |
||
226 | */ |
||
227 | final public function testOverrideMethodStubFailure() |
||
228 | { |
||
229 | $this->strategy->decorate($this->mock, [ |
||
230 | 'getInt' => mt_rand(), |
||
231 | 'throwException' => mt_rand() |
||
232 | ]); |
||
233 | |||
234 | $this->assertSame( |
||
235 | $this->namesWithValues['getInt'], |
||
236 | $this->strategy->get($this->mock)->getInt() |
||
237 | ); |
||
238 | |||
239 | $this->assertSame( |
||
240 | $this->namesWithValues['getInt'], |
||
241 | $this->strategy->get($this->mock)->getInt() |
||
242 | ); |
||
243 | |||
244 | $this->expectException(\Exception::class); |
||
245 | $this->expectExceptionMessage($this->namesWithValues['throwException']->getMessage()); |
||
246 | |||
247 | $this->strategy->get($this->mock)->throwException(); |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * @throws \Exception |
||
252 | */ |
||
253 | final public function testCallMethodWithArgumentSuccess() |
||
254 | { |
||
255 | $this->assertSame( |
||
256 | $this->namesWithValues['withArgument'], |
||
257 | $this->strategy->get($this->mock)->withArgument(mt_rand()) |
||
258 | ); |
||
259 | } |
||
260 | |||
261 | /** |
||
262 | * @return void |
||
263 | */ |
||
264 | final public function testCallMethodWithoutArgumentFailure() |
||
265 | { |
||
266 | $this->expectException(\Error::class); |
||
267 | |||
268 | $this->strategy->get($this->mock)->withArgument(); |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * @return void |
||
273 | */ |
||
274 | final public function testCallMethodWithWrongArgumentFailure() |
||
275 | { |
||
276 | $this->expectException(\TypeError::class); |
||
277 | |||
278 | $this->strategy->get($this->mock)->withArgument('string'); |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * @return void |
||
283 | */ |
||
284 | final public function testGetFakeMockFailure() |
||
285 | { |
||
286 | $this->expectException(InvalidArgumentException::class); |
||
287 | |||
288 | $this->strategy->get(new \stdClass()); |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * @return array |
||
293 | */ |
||
294 | final public function fqcnProvider(): array |
||
295 | { |
||
296 | $required = [ |
||
297 | ['an interface', TestInterface::class], |
||
298 | ['an abstract class', AbstractTestClass::class], |
||
299 | ['a class', $this->getRandomFQCN()] |
||
300 | ]; |
||
301 | |||
302 | $optional = [ |
||
303 | ['an empty FQCN', self::FQCN_EMPTY], |
||
304 | ['an invalid FQCN', self::FQCN_INVALID], |
||
305 | ['a nonexistent FQCN', $this->getNonexistentFQCN()], |
||
306 | ['multiple interfaces', FooTestInterface::class, BarTestInterface::class], |
||
307 | ['class and interface', FooTestClass::class, BarTestInterface::class], |
||
308 | ['multiple classes', FooTestClass::class, BarTestClass::class], |
||
309 | ['multiple nonexistent FQCNs', $this->getNonexistentFQCN(), $this->getNonexistentFQCN()] |
||
310 | ]; |
||
311 | |||
312 | $data = array_merge( |
||
313 | array_map(function (array $set) { |
||
314 | array_unshift($set, $required = true); |
||
315 | return $set; |
||
316 | }, $required), |
||
317 | array_map(function (array $set) { |
||
318 | array_unshift($set, $required = false); |
||
319 | return $set; |
||
320 | }, $optional) |
||
321 | ); |
||
322 | |||
323 | return array_reduce($data, function (array $data, array $set) { |
||
324 | $key = preg_replace('/^an? +/', '', $set[1]); |
||
325 | $data[$key] = $set; |
||
326 | |||
327 | return $data; |
||
328 | }, []); |
||
329 | } |
||
330 | |||
331 | /** |
||
332 | * @param MockingStrategyInterface $strategy |
||
333 | */ |
||
334 | final protected function setStrategy(MockingStrategyInterface $strategy) |
||
335 | { |
||
336 | $this->strategy = $strategy; |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * @return string |
||
341 | */ |
||
342 | final protected function getRandomFQCN(): string |
||
343 | { |
||
344 | return [ |
||
345 | FooTestClass::class, |
||
346 | BarTestClass::class |
||
347 | ][random_int(0, 1)]; |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * @return string |
||
352 | */ |
||
353 | final protected function getNonexistentFQCN(): string |
||
354 | { |
||
355 | return sprintf( |
||
356 | self::FQCN_NONEXISTENT_TEMPLATE, |
||
357 | mt_rand() |
||
358 | ); |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * @param $mock |
||
363 | * @throws \Exception |
||
364 | * @throws \Moka\Exception\NotImplementedException |
||
365 | */ |
||
366 | final protected function checkMock($mock) |
||
367 | { |
||
368 | $this->assertInstanceOf($this->strategy->getMockType(), $mock); |
||
369 | } |
||
370 | |||
371 | /** |
||
372 | * @param string $feature |
||
373 | */ |
||
374 | final protected function markFeatureUnsupported(string $feature) |
||
375 | { |
||
376 | $this->markTestSkipped( |
||
377 | sprintf( |
||
378 | 'Strategy "%s" doesn\'t support %s', |
||
379 | \get_class($this->strategy), |
||
380 | $feature |
||
381 | ) |
||
382 | ); |
||
383 | } |
||
384 | |||
385 | /** |
||
386 | * @param string[] ...$fqcns |
||
387 | * @throws \Exception |
||
388 | * @throws \Moka\Exception\NotImplementedException |
||
389 | */ |
||
390 | private function buildAndGet(string ...$fqcns) |
||
391 | { |
||
392 | $this->checkMock( |
||
393 | $mock = $this->strategy->build(implode(', ', $fqcns)) |
||
394 | ); |
||
395 | |||
396 | $object = $this->strategy->get($mock); |
||
397 | foreach ($fqcns as $fqcn) { |
||
398 | $this->assertInstanceOf($fqcn, $object); |
||
0 ignored issues
–
show
$fqcn is of type array<integer,string> , but the function expects a string .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
Loading history...
|
|||
399 | } |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * @param string $fqcnType |
||
404 | * @param string[] ...$fqcns |
||
405 | * @throws \Exception |
||
406 | * @throws \Moka\Exception\NotImplementedException |
||
407 | */ |
||
408 | private function tryBuildAndGet(string $fqcnType, string ...$fqcns) |
||
409 | { |
||
410 | try { |
||
411 | $this->checkMock( |
||
412 | $mock = $this->strategy->build(implode(', ', $fqcns)) |
||
413 | ); |
||
414 | } catch (MockNotCreatedException $e) { |
||
415 | $this->markFeatureUnsupported( |
||
416 | sprintf( |
||
417 | 'building with %s: %s', |
||
418 | $fqcnType, |
||
419 | $e->getMessage() |
||
420 | ) |
||
421 | ); |
||
422 | |||
423 | return; |
||
424 | } |
||
425 | |||
426 | $object = $this->strategy->get($mock); |
||
427 | foreach ($fqcns as $fqcn) { |
||
428 | if (!is_a($object, $fqcn)) { |
||
429 | $this->markFeatureUnsupported( |
||
430 | sprintf( |
||
431 | 'getting a valid mock from %s', |
||
432 | $fqcnType |
||
433 | ) |
||
434 | ); |
||
435 | |||
436 | return; |
||
437 | } |
||
438 | } |
||
439 | } |
||
440 | } |
||
441 |
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.