Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like FiltererTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use FiltererTest, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
15 | final class FiltererTest extends TestCase |
||
16 | { |
||
17 | public function setUp() |
||
21 | |||
22 | /** |
||
23 | * @test |
||
24 | * @covers ::filter |
||
25 | * @dataProvider provideValidFilterData |
||
26 | * |
||
27 | * @param array $spec The filter specification to be use. |
||
28 | * @param array $input The input to be filtered. |
||
29 | * @param array $options The filterer options |
||
30 | * @param array $expectedResult The expected filterer result. |
||
31 | */ |
||
32 | public function filter(array $spec, array $input, array $options, array $expectedResult) |
||
36 | |||
37 | public function provideValidFilterData() : array |
||
227 | |||
228 | /** |
||
229 | * @test |
||
230 | * @covers ::filter |
||
231 | */ |
||
232 | public function filterReturnsResponseType() |
||
247 | |||
248 | /** |
||
249 | * @test |
||
250 | * @covers ::filter |
||
251 | */ |
||
252 | public function filterReturnsResponseTypeWithErrors() |
||
267 | |||
268 | /** |
||
269 | * @test |
||
270 | * @covers ::filter |
||
271 | * @covers ::setFilterAliases |
||
272 | */ |
||
273 | public function filterCustomShortNamePass() |
||
279 | |||
280 | /** |
||
281 | * @test |
||
282 | * @covers ::filter |
||
283 | * @covers ::setFilterAliases |
||
284 | * @covers ::getFilterAliases |
||
285 | */ |
||
286 | public function filterGetSetKnownFilters() |
||
292 | |||
293 | /** |
||
294 | * @test |
||
295 | * @covers ::filter |
||
296 | * @expectedException Exception |
||
297 | * @expectedExceptionMessage Function 'boo' for field 'foo' is not callable |
||
298 | */ |
||
299 | public function notCallable() |
||
303 | |||
304 | /** |
||
305 | * @test |
||
306 | * @covers ::filter |
||
307 | * @expectedException InvalidArgumentException |
||
308 | * @expectedExceptionMessage 'allowUnknowns' option was not a bool |
||
309 | */ |
||
310 | public function allowUnknownsNotBool() |
||
314 | |||
315 | /** |
||
316 | * @test |
||
317 | * @covers ::filter |
||
318 | * @expectedException InvalidArgumentException |
||
319 | * @expectedExceptionMessage 'defaultRequired' option was not a bool |
||
320 | */ |
||
321 | public function defaultRequiredNotBool() |
||
325 | |||
326 | /** |
||
327 | * @test |
||
328 | * @covers ::filter |
||
329 | */ |
||
330 | public function filterThrowsExceptionOnInvalidResponseType() |
||
337 | |||
338 | /** |
||
339 | * @test |
||
340 | * @covers ::filter |
||
341 | * @expectedException InvalidArgumentException |
||
342 | * @expectedExceptionMessage filters for field 'boo' was not a array |
||
343 | */ |
||
344 | public function filtersNotArrayInLeftOverSpec() |
||
348 | |||
349 | /** |
||
350 | * @test |
||
351 | * @covers ::filter |
||
352 | * @expectedException InvalidArgumentException |
||
353 | * @expectedExceptionMessage filters for field 'boo' was not a array |
||
354 | */ |
||
355 | public function filtersNotArrayWithInput() |
||
359 | |||
360 | /** |
||
361 | * @test |
||
362 | * @covers ::filter |
||
363 | * @expectedException InvalidArgumentException |
||
364 | * @expectedExceptionMessage filter for field 'boo' was not a array |
||
365 | */ |
||
366 | public function filterNotArray() |
||
370 | |||
371 | /** |
||
372 | * @test |
||
373 | * @covers ::filter |
||
374 | * @expectedException InvalidArgumentException |
||
375 | * @expectedExceptionMessage 'required' for field 'boo' was not a bool |
||
376 | */ |
||
377 | public function requiredNotBool() |
||
381 | |||
382 | /** |
||
383 | * @test |
||
384 | * @covers ::registerAlias |
||
385 | * @expectedException InvalidArgumentException |
||
386 | * @expectedExceptionMessage $alias was not a string or int |
||
387 | */ |
||
388 | public function registerAliasAliasNotString() |
||
392 | |||
393 | /** |
||
394 | * @test |
||
395 | * @covers ::registerAlias |
||
396 | * @expectedException Exception |
||
397 | * @expectedExceptionMessage Alias 'upper' exists |
||
398 | */ |
||
399 | public function registerExistingAliasOverwriteFalse() |
||
405 | |||
406 | /** |
||
407 | * @test |
||
408 | * @covers ::registerAlias |
||
409 | */ |
||
410 | public function registerExistingAliasOverwriteTrue() |
||
416 | |||
417 | public static function failingFilter() |
||
421 | |||
422 | public static function passingFilter($value) |
||
426 | |||
427 | /** |
||
428 | * Verify behavior of filter() when 'error' is not a string value. |
||
429 | * |
||
430 | * @test |
||
431 | * @covers ::filter |
||
432 | * @expectedException InvalidArgumentException |
||
433 | * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string |
||
434 | * |
||
435 | * @return void |
||
436 | */ |
||
437 | View Code Duplication | public function filterWithNonStringError() |
|
444 | |||
445 | /** |
||
446 | * Verify behavior of filter() when 'error' is an empty string. |
||
447 | * |
||
448 | * @test |
||
449 | * @covers ::filter |
||
450 | * @expectedException InvalidArgumentException |
||
451 | * @expectedExceptionMessage error for field 'fieldOne' was not a non-empty string |
||
452 | * |
||
453 | * @return void |
||
454 | */ |
||
455 | View Code Duplication | public function filterWithEmptyStringError() |
|
462 | |||
463 | /** |
||
464 | * @test |
||
465 | * @covers ::ofScalars |
||
466 | */ |
||
467 | public function ofScalars() |
||
471 | |||
472 | /** |
||
473 | * @test |
||
474 | * @covers ::ofScalars |
||
475 | */ |
||
476 | public function ofScalarsChained() |
||
480 | |||
481 | /** |
||
482 | * @test |
||
483 | * @covers ::ofScalars |
||
484 | */ |
||
485 | public function ofScalarsWithMeaninglessKeys() |
||
489 | |||
490 | /** |
||
491 | * @test |
||
492 | * @covers ::ofScalars |
||
493 | */ |
||
494 | View Code Duplication | public function ofScalarsFail() |
|
511 | |||
512 | /** |
||
513 | * @test |
||
514 | * @covers ::ofArrays |
||
515 | */ |
||
516 | public function ofArrays() |
||
521 | |||
522 | /** |
||
523 | * @test |
||
524 | * @covers ::ofArrays |
||
525 | */ |
||
526 | public function ofArraysChained() |
||
532 | |||
533 | /** |
||
534 | * @test |
||
535 | * @covers ::ofArrays |
||
536 | */ |
||
537 | View Code Duplication | public function ofArraysRequiredAndUnknown() |
|
547 | |||
548 | /** |
||
549 | * @test |
||
550 | * @covers ::ofArrays |
||
551 | */ |
||
552 | public function ofArraysFail() |
||
574 | |||
575 | /** |
||
576 | * @test |
||
577 | * @covers ::ofArray |
||
578 | */ |
||
579 | public function ofArray() |
||
585 | |||
586 | /** |
||
587 | * @test |
||
588 | * @covers ::ofArray |
||
589 | */ |
||
590 | public function ofArrayChained() |
||
596 | |||
597 | /** |
||
598 | * @test |
||
599 | * @covers ::ofArray |
||
600 | */ |
||
601 | public function ofArrayRequiredSuccess() |
||
607 | |||
608 | /** |
||
609 | * @test |
||
610 | * @covers ::ofArray |
||
611 | */ |
||
612 | View Code Duplication | public function ofArrayRequiredFail() |
|
622 | |||
623 | /** |
||
624 | * @test |
||
625 | * @covers ::ofArray |
||
626 | */ |
||
627 | View Code Duplication | public function ofArrayUnknown() |
|
637 | |||
638 | /** |
||
639 | * @test |
||
640 | * @covers ::__invoke |
||
641 | * @dataProvider provideInvoke |
||
642 | * |
||
643 | * @param array $filter |
||
644 | * @param array $options |
||
645 | * @param mixed $value |
||
646 | * @param array $expected |
||
647 | */ |
||
648 | public function invoke(array $filter, array $options, $value, array $expected) |
||
655 | |||
656 | /** |
||
657 | * @returns array |
||
658 | */ |
||
659 | public function provideInvoke() : array |
||
676 | |||
677 | /** |
||
678 | * @test |
||
679 | * @covers ::__invoke |
||
680 | */ |
||
681 | public function invokeThrowsFilterException() |
||
693 | |||
694 | /** |
||
695 | * @test |
||
696 | * @covers ::execute |
||
697 | * @dataProvider provideExecute |
||
698 | * |
||
699 | * @param array $filter |
||
700 | * @param array $options |
||
701 | * @param mixed $value |
||
702 | * @param array $expected |
||
703 | */ |
||
704 | public function execute(array $filter, array $options, $value, array $expected) |
||
711 | |||
712 | /** |
||
713 | * @returns array |
||
714 | */ |
||
715 | public function provideExecute() : array |
||
744 | } |
||
745 |
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: