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 ParserTest 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 ParserTest, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 18 | class ParserTest extends \PHPUnit_Framework_TestCase |
||
| 19 | { |
||
| 20 | protected $parser; |
||
| 21 | |||
| 22 | protected function setUp() |
||
| 26 | |||
| 27 | protected function tearDown() |
||
| 31 | |||
| 32 | /** |
||
| 33 | * @dataProvider getDataFormSpecifications |
||
| 34 | */ |
||
| 35 | public function testSpecifications($file, $expected, $yaml, $comment) |
||
| 39 | |||
| 40 | public function getDataFormSpecifications() |
||
| 69 | |||
| 70 | public function testTabsInYaml() |
||
| 91 | |||
| 92 | public function testEndOfTheDocumentMarker() |
||
| 102 | |||
| 103 | public function getBlockChompingTests() |
||
| 393 | |||
| 394 | /** |
||
| 395 | * @dataProvider getBlockChompingTests |
||
| 396 | */ |
||
| 397 | public function testBlockChomping($expected, $yaml) |
||
| 401 | |||
| 402 | /** |
||
| 403 | * Regression test for issue #7989. |
||
| 404 | * |
||
| 405 | * @see https://github.com/symfony/symfony/issues/7989 |
||
| 406 | */ |
||
| 407 | public function testBlockLiteralWithLeadingNewlines() |
||
| 422 | |||
| 423 | public function testObjectSupportEnabled() |
||
| 437 | |||
| 438 | /** |
||
| 439 | * @dataProvider invalidDumpedObjectProvider |
||
| 440 | */ |
||
| 441 | public function testObjectSupportDisabledButNoExceptions($input) |
||
| 445 | |||
| 446 | /** |
||
| 447 | * @dataProvider getObjectForMapTests |
||
| 448 | */ |
||
| 449 | public function testObjectForMap($yaml, $expected) |
||
| 450 | { |
||
| 451 | $this->assertEquals($expected, $this->parser->parse($yaml, false, false, true)); |
||
| 452 | } |
||
| 453 | |||
| 454 | public function getObjectForMapTests() |
||
| 455 | { |
||
| 456 | $tests = array(); |
||
| 457 | |||
| 458 | $yaml = <<<EOF |
||
| 459 | foo: |
||
| 460 | fiz: [cat] |
||
| 461 | EOF; |
||
| 462 | $expected = new \stdClass(); |
||
| 463 | $expected->foo = new \stdClass(); |
||
| 464 | $expected->foo->fiz = array('cat'); |
||
| 465 | $tests['mapping'] = array($yaml, $expected); |
||
| 466 | |||
| 467 | $yaml = '{ "foo": "bar", "fiz": "cat" }'; |
||
| 468 | $expected = new \stdClass(); |
||
| 469 | $expected->foo = 'bar'; |
||
| 470 | $expected->fiz = 'cat'; |
||
| 471 | $tests['inline-mapping'] = array($yaml, $expected); |
||
| 472 | |||
| 473 | $yaml = "foo: bar\nbaz: foobar"; |
||
| 474 | $expected = new \stdClass(); |
||
| 475 | $expected->foo = 'bar'; |
||
| 476 | $expected->baz = 'foobar'; |
||
| 477 | $tests['object-for-map-is-applied-after-parsing'] = array($yaml, $expected); |
||
| 478 | |||
| 479 | $yaml = <<<EOT |
||
| 480 | array: |
||
| 481 | - key: one |
||
| 482 | - key: two |
||
| 483 | EOT; |
||
| 484 | $expected = new \stdClass(); |
||
| 485 | $expected->array = array(); |
||
| 486 | $expected->array[0] = new \stdClass(); |
||
| 487 | $expected->array[0]->key = 'one'; |
||
| 488 | $expected->array[1] = new \stdClass(); |
||
| 489 | $expected->array[1]->key = 'two'; |
||
| 490 | $tests['nest-map-and-sequence'] = array($yaml, $expected); |
||
| 491 | |||
| 492 | $yaml = <<<YAML |
||
| 493 | map: |
||
| 494 | 1: one |
||
| 495 | 2: two |
||
| 496 | YAML; |
||
| 497 | $expected = new \stdClass(); |
||
| 498 | $expected->map = new \stdClass(); |
||
| 499 | $expected->map->{1} = 'one'; |
||
| 500 | $expected->map->{2} = 'two'; |
||
| 501 | $tests['numeric-keys'] = array($yaml, $expected); |
||
| 502 | |||
| 503 | $yaml = <<<YAML |
||
| 504 | map: |
||
| 505 | 0: one |
||
| 506 | 1: two |
||
| 507 | YAML; |
||
| 508 | $expected = new \stdClass(); |
||
| 509 | $expected->map = new \stdClass(); |
||
| 510 | $expected->map->{0} = 'one'; |
||
| 511 | $expected->map->{1} = 'two'; |
||
| 512 | $tests['zero-indexed-numeric-keys'] = array($yaml, $expected); |
||
| 513 | |||
| 514 | return $tests; |
||
| 515 | } |
||
| 516 | |||
| 517 | /** |
||
| 518 | * @dataProvider invalidDumpedObjectProvider |
||
| 519 | * @expectedException \Symfony\Component\Yaml\Exception\ParseException |
||
| 520 | */ |
||
| 521 | public function testObjectsSupportDisabledWithExceptions($yaml) |
||
| 525 | |||
| 526 | public function invalidDumpedObjectProvider() |
||
| 527 | { |
||
| 528 | $yamlTag = <<<EOF |
||
| 529 | foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";} |
||
| 530 | bar: 1 |
||
| 531 | EOF; |
||
| 532 | $localTag = <<<EOF |
||
| 533 | foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";} |
||
| 534 | bar: 1 |
||
| 535 | EOF; |
||
| 536 | |||
| 537 | return array( |
||
| 538 | 'yaml-tag' => array($yamlTag), |
||
| 539 | 'local-tag' => array($localTag), |
||
| 540 | ); |
||
| 541 | } |
||
| 542 | |||
| 543 | /** |
||
| 544 | * @requires extension iconv |
||
| 545 | */ |
||
| 546 | public function testNonUtf8Exception() |
||
| 564 | |||
| 565 | /** |
||
| 566 | * @expectedException \Symfony\Component\Yaml\Exception\ParseException |
||
| 567 | */ |
||
| 568 | public function testUnindentedCollectionException() |
||
| 581 | |||
| 582 | /** |
||
| 583 | * @expectedException \Symfony\Component\Yaml\Exception\ParseException |
||
| 584 | */ |
||
| 585 | public function testShortcutKeyUnindentedCollectionException() |
||
| 597 | |||
| 598 | /** |
||
| 599 | * @expectedException \Symfony\Component\Yaml\Exception\ParseException |
||
| 600 | * @expectedExceptionMessageRegExp /^Multiple documents are not supported.+/ |
||
| 601 | */ |
||
| 602 | public function testMultipleDocumentsNotSupportedException() |
||
| 618 | |||
| 619 | /** |
||
| 620 | * @expectedException \Symfony\Component\Yaml\Exception\ParseException |
||
| 621 | */ |
||
| 622 | public function testSequenceInAMapping() |
||
| 631 | |||
| 632 | public function testSequenceInMappingStartedBySingleDashLine() |
||
| 659 | |||
| 660 | public function testSequenceFollowedByCommentEmbeddedInMapping() |
||
| 661 | { |
||
| 662 | $yaml = <<<EOT |
||
| 663 | a: |
||
| 664 | b: |
||
| 665 | - c |
||
| 666 | # comment |
||
| 667 | d: e |
||
| 668 | EOT; |
||
| 669 | $expected = array( |
||
| 670 | 'a' => array( |
||
| 671 | 'b' => array('c'), |
||
| 672 | 'd' => 'e', |
||
| 673 | ), |
||
| 674 | ); |
||
| 675 | |||
| 676 | $this->assertSame($expected, $this->parser->parse($yaml)); |
||
| 677 | } |
||
| 678 | |||
| 679 | /** |
||
| 680 | * @expectedException \Symfony\Component\Yaml\Exception\ParseException |
||
| 681 | */ |
||
| 682 | public function testMappingInASequence() |
||
| 691 | |||
| 692 | /** |
||
| 693 | * @expectedException \Symfony\Component\Yaml\Exception\ParseException |
||
| 694 | * @expectedExceptionMessage missing colon |
||
| 695 | */ |
||
| 696 | public function testScalarInSequence() |
||
| 697 | { |
||
| 698 | Yaml::parse(<<<EOF |
||
| 699 | foo: |
||
| 700 | - bar |
||
| 701 | "missing colon" |
||
| 702 | foo: bar |
||
| 703 | EOF |
||
| 704 | ); |
||
| 705 | } |
||
| 706 | |||
| 707 | /** |
||
| 708 | * > It is an error for two equal keys to appear in the same mapping node. |
||
| 709 | * > In such a case the YAML processor may continue, ignoring the second |
||
| 710 | * > `key: value` pair and issuing an appropriate warning. This strategy |
||
| 711 | * > preserves a consistent information model for one-pass and random access |
||
| 712 | * > applications. |
||
| 713 | * |
||
| 714 | * @see http://yaml.org/spec/1.2/spec.html#id2759572 |
||
| 715 | * @see http://yaml.org/spec/1.1/#id932806 |
||
| 716 | */ |
||
| 717 | View Code Duplication | public function testMappingDuplicateKeyBlock() |
|
| 734 | |||
| 735 | View Code Duplication | public function testMappingDuplicateKeyFlow() |
|
| 748 | |||
| 749 | public function testEmptyValue() |
||
| 757 | |||
| 758 | public function testCommentAtTheRootIndent() |
||
| 759 | { |
||
| 760 | $this->assertEquals(array( |
||
| 761 | 'services' => array( |
||
| 762 | 'app.foo_service' => array( |
||
| 763 | 'class' => 'Foo', |
||
| 764 | ), |
||
| 765 | 'app/bar_service' => array( |
||
| 766 | 'class' => 'Bar', |
||
| 767 | ), |
||
| 768 | ), |
||
| 769 | ), Yaml::parse(<<<'EOF' |
||
| 770 | # comment 1 |
||
| 771 | services: |
||
| 772 | # comment 2 |
||
| 773 | # comment 3 |
||
| 774 | app.foo_service: |
||
| 775 | class: Foo |
||
| 776 | # comment 4 |
||
| 777 | # comment 5 |
||
| 778 | app/bar_service: |
||
| 779 | class: Bar |
||
| 780 | EOF |
||
| 781 | )); |
||
| 782 | } |
||
| 783 | |||
| 784 | public function testStringBlockWithComments() |
||
| 811 | |||
| 812 | public function testFoldedStringBlockWithComments() |
||
| 840 | |||
| 841 | public function testNestedFoldedStringBlockWithComments() |
||
| 872 | |||
| 873 | public function testReferenceResolvingInInlineStrings() |
||
| 898 | |||
| 899 | View Code Duplication | public function testYamlDirective() |
|
| 909 | |||
| 910 | View Code Duplication | public function testFloatKeys() |
|
| 911 | { |
||
| 912 | $yaml = <<<'EOF' |
||
| 913 | foo: |
||
| 914 | 1.2: "bar" |
||
| 915 | 1.3: "baz" |
||
| 916 | EOF; |
||
| 917 | |||
| 918 | $expected = array( |
||
| 919 | 'foo' => array( |
||
| 920 | '1.2' => 'bar', |
||
| 921 | '1.3' => 'baz', |
||
| 922 | ), |
||
| 923 | ); |
||
| 924 | |||
| 925 | $this->assertEquals($expected, $this->parser->parse($yaml)); |
||
| 926 | } |
||
| 927 | |||
| 928 | /** |
||
| 929 | * @group legacy |
||
| 930 | * throw ParseException in Symfony 3.0 |
||
| 931 | * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered |
||
| 932 | */ |
||
| 933 | public function testColonInMappingValueException() |
||
| 934 | { |
||
| 935 | $parser = $this->parser; |
||
| 936 | |||
| 937 | ErrorAssert::assertDeprecationsAreTriggered('Using a colon in the unquoted mapping value "bar: baz" in line 1 is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', function () use ($parser) { |
||
| 938 | $yaml = <<<EOF |
||
| 939 | foo: bar: baz |
||
| 940 | EOF; |
||
| 941 | $parser->parse($yaml); |
||
| 942 | }); |
||
| 943 | } |
||
| 944 | |||
| 945 | public function testColonInMappingValueExceptionNotTriggeredByColonInComment() |
||
| 946 | { |
||
| 947 | $yaml = <<<EOT |
||
| 948 | foo: |
||
| 949 | bar: foobar # Note: a comment after a colon |
||
| 950 | EOT; |
||
| 951 | |||
| 952 | $this->assertSame(array('foo' => array('bar' => 'foobar')), $this->parser->parse($yaml)); |
||
| 953 | } |
||
| 954 | |||
| 955 | /** |
||
| 956 | * @dataProvider getCommentLikeStringInScalarBlockData |
||
| 957 | */ |
||
| 958 | public function testCommentLikeStringsAreNotStrippedInBlockScalars($yaml, $expectedParserResult) |
||
| 962 | |||
| 963 | public function getCommentLikeStringInScalarBlockData() |
||
| 1088 | |||
| 1089 | public function testBlankLinesAreParsedAsNewLinesInFoldedBlocks() |
||
| 1112 | |||
| 1113 | public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks() |
||
| 1139 | |||
| 1140 | /** |
||
| 1141 | * @param $lineNumber |
||
| 1142 | * @param $yaml |
||
| 1143 | * @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider |
||
| 1144 | */ |
||
| 1145 | public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml) |
||
| 1146 | { |
||
| 1147 | $this->setExpectedException( |
||
| 1148 | '\Symfony\Component\Yaml\Exception\ParseException', |
||
| 1149 | sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber) |
||
| 1150 | ); |
||
| 1151 | |||
| 1152 | $this->parser->parse($yaml); |
||
| 1153 | } |
||
| 1154 | |||
| 1155 | public function parserThrowsExceptionWithCorrectLineNumberProvider() |
||
| 1156 | { |
||
| 1157 | return array( |
||
| 1158 | array( |
||
| 1159 | 4, |
||
| 1160 | <<<YAML |
||
| 1161 | foo: |
||
| 1162 | - |
||
| 1163 | # bar |
||
| 1164 | bar: "123", |
||
| 1165 | YAML |
||
| 1166 | ), |
||
| 1167 | array( |
||
| 1168 | 5, |
||
| 1169 | <<<YAML |
||
| 1170 | foo: |
||
| 1171 | - |
||
| 1172 | # bar |
||
| 1173 | # bar |
||
| 1174 | bar: "123", |
||
| 1175 | YAML |
||
| 1176 | ), |
||
| 1177 | array( |
||
| 1178 | 8, |
||
| 1179 | <<<YAML |
||
| 1180 | foo: |
||
| 1181 | - |
||
| 1182 | # foobar |
||
| 1183 | baz: 123 |
||
| 1184 | bar: |
||
| 1185 | - |
||
| 1186 | # bar |
||
| 1187 | bar: "123", |
||
| 1188 | YAML |
||
| 1189 | ), |
||
| 1190 | array( |
||
| 1191 | 10, |
||
| 1192 | <<<YAML |
||
| 1193 | foo: |
||
| 1194 | - |
||
| 1195 | # foobar |
||
| 1196 | # foobar |
||
| 1197 | baz: 123 |
||
| 1198 | bar: |
||
| 1199 | - |
||
| 1200 | # bar |
||
| 1201 | # bar |
||
| 1202 | bar: "123", |
||
| 1203 | YAML |
||
| 1204 | ), |
||
| 1205 | ); |
||
| 1206 | } |
||
| 1207 | } |
||
| 1208 | |||
| 1213 |
There are different options of fixing this problem.
If you want to be on the safe side, you can add an additional type-check:
If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:
Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.