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 TestOfAnnotationMatchers 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 TestOfAnnotationMatchers, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 57 | class TestOfAnnotationMatchers extends UnitTestCase { |
||
| 58 | public function testAnnotationsMatcherShouldMatchAnnotationWithGarbage() { |
||
| 59 | $expected = array('Annotation' => array( |
||
| 60 | array('value' => true), |
||
| 61 | )); |
||
| 62 | $matcher = new AnnotationsMatcher; |
||
| 63 | $this->assertMatcherResult($matcher, '/** asd bla bla @Annotation(true) */@', $expected); |
||
| 64 | } |
||
| 65 | |||
| 66 | public function testAnnotationsMatcherShouldNotMatchEmail() { |
||
| 67 | $matcher = new AnnotationsMatcher; |
||
| 68 | $this->assertMatcherResult($matcher, '[email protected]', array()); |
||
| 69 | } |
||
| 70 | |||
| 71 | public function testAnnotationsMatcherShouldMatchMultipleAnnotations() { |
||
| 72 | $expected = array('Annotation' => array( |
||
| 73 | array('value' => true), |
||
| 74 | array('value' => false) |
||
| 75 | )); |
||
| 76 | $matcher = new AnnotationsMatcher; |
||
| 77 | $this->assertMatcherResult($matcher, ' ss @Annotation(true) @Annotation(false)', $expected); |
||
| 78 | } |
||
| 79 | |||
| 80 | public function testAnnotationsMatcherShouldMatchMultipleAnnotationsOnManyLines() { |
||
| 81 | $expected = array('Annotation' => array( |
||
| 82 | array('value' => true), |
||
| 83 | array('value' => false) |
||
| 84 | )); |
||
| 85 | $block = "/** |
||
| 86 | @Annotation(true) |
||
| 87 | @Annotation(false) |
||
| 88 | **/"; |
||
| 89 | $matcher = new AnnotationsMatcher; |
||
| 90 | $this->assertMatcherResult($matcher, $block, $expected); |
||
| 91 | } |
||
| 92 | |||
| 93 | public function testAnnotationMatcherShouldMatchMultilineAnnotations() { |
||
| 94 | $block= '/** |
||
| 95 | * @Annotation( |
||
| 96 | paramOne="value1", |
||
| 97 | paramTwo={ |
||
| 98 | "value2" , |
||
| 99 | {"one", "two"} |
||
| 100 | }, |
||
| 101 | paramThree="three" |
||
| 102 | ) |
||
| 103 | */'; |
||
| 104 | $expected = array('Annotation' => array( |
||
| 105 | array( |
||
| 106 | 'paramOne' => 'value1', |
||
| 107 | 'paramTwo' => array('value2', array('one', 'two')), |
||
| 108 | 'paramThree' => 'three', |
||
| 109 | ) |
||
| 110 | )); |
||
| 111 | $matcher = new AnnotationsMatcher; |
||
| 112 | $this->assertMatcherResult($matcher, $block, $expected); |
||
| 113 | } |
||
| 114 | |||
| 115 | public function testAnnotationMatcherShouldMatchSimpleAnnotation() { |
||
| 116 | $matcher = new AnnotationMatcher; |
||
| 117 | $this->assertNotFalse($matcher->matches('@Namespace_Annotation', $value)); |
||
| 118 | $this->assertEqual($value, array('Namespace_Annotation', array())); |
||
| 119 | } |
||
| 120 | |||
| 121 | public function testAnnotationMatcherShouldNotMatchAnnotationWithSmallStartingLetter() { |
||
| 122 | $matcher = new AnnotationMatcher; |
||
| 123 | $this->assertFalse($matcher->matches('@annotation', $value)); |
||
| 124 | } |
||
| 125 | |||
| 126 | public function testAnnotationMatcherShouldMatchShortAnnotations() { |
||
| 127 | $matcher = new AnnotationMatcher; |
||
| 128 | $this->assertMatcherResult($matcher, '@X', array('X', array())); |
||
| 129 | } |
||
| 130 | |||
| 131 | public function testAnnotationMatcherShouldMatchAlsoBrackets() { |
||
| 132 | $matcher = new AnnotationMatcher; |
||
| 133 | $this->assertEqual($matcher->matches('@Annotation()', $value), 13); |
||
| 134 | $this->assertEqual($value, array('Annotation', array())); |
||
| 135 | } |
||
| 136 | |||
| 137 | public function testAnnotationMatcherShouldMatchValuedAnnotation() { |
||
| 138 | $matcher = new AnnotationMatcher; |
||
| 139 | $this->assertMatcherResult($matcher, '@Annotation(true)', array('Annotation', array('value' => true))); |
||
| 140 | } |
||
| 141 | |||
| 142 | public function testAnnotationMatcherShouldMatchMultiValuedAnnotation() { |
||
| 143 | $matcher = new AnnotationMatcher; |
||
| 144 | $this->assertMatcherResult($matcher, '@Annotation(key=true, key2=3.14)', array('Annotation', array('key' => true, 'key2' => 3.14))); |
||
| 145 | } |
||
| 146 | |||
| 147 | public function testParametersMatcherShouldMatchEmptyStringAndReturnEmptyArray() { |
||
| 148 | $matcher = new AnnotationParametersMatcher; |
||
| 149 | $this->assertIdentical($matcher->matches('', $value), 0); |
||
| 150 | $this->assertEqual($value, array()); |
||
| 151 | } |
||
| 152 | |||
| 153 | public function testParametersMatcherShouldMatchEmptyBracketsAndReturnEmptyArray() { |
||
| 154 | $matcher = new AnnotationParametersMatcher; |
||
| 155 | $this->assertIdentical($matcher->matches('()', $value), 2); |
||
| 156 | $this->assertEqual($value, array()); |
||
| 157 | } |
||
| 158 | |||
| 159 | public function testParametersMatcherShouldMatchMultilinedParameters() { |
||
| 160 | $matcher = new AnnotationParametersMatcher; |
||
| 161 | $block = "( |
||
| 162 | key = true, |
||
| 163 | key2 = false |
||
| 164 | )"; |
||
| 165 | $this->assertMatcherResult($matcher, $block, array('key' => true, 'key2' => false)); |
||
| 166 | } |
||
| 167 | |||
| 168 | public function testValuesMatcherShouldMatchSimpleValueOrHash() { |
||
| 169 | $matcher = new AnnotationValuesMatcher; |
||
| 170 | $this->assertNotFalse($matcher->matches('true', $value)); |
||
| 171 | $this->assertNotFalse($matcher->matches('key=true', $value)); |
||
| 172 | } |
||
| 173 | |||
| 174 | public function testValueMatcherShouldMatchConstants() { |
||
| 175 | $matcher = new AnnotationValueMatcher; |
||
| 176 | $this->assertMatcherResult($matcher, 'true', true); |
||
| 177 | $this->assertMatcherResult($matcher, 'false', false); |
||
| 178 | $this->assertMatcherResult($matcher, 'TRUE', true); |
||
| 179 | $this->assertMatcherResult($matcher, 'FALSE', false); |
||
| 180 | $this->assertMatcherResult($matcher, 'NULL', null); |
||
| 181 | $this->assertMatcherResult($matcher, 'null', null); |
||
| 182 | } |
||
| 183 | |||
| 184 | public function testValueMatcherShouldMatchStrings() { |
||
| 185 | $matcher = new AnnotationValueMatcher; |
||
| 186 | $this->assertMatcherResult($matcher, '"string"', 'string'); |
||
| 187 | $this->assertMatcherResult($matcher, "'string'", 'string'); |
||
| 188 | } |
||
| 189 | |||
| 190 | public function testValueMatcherShouldMatchNumbers() { |
||
| 191 | $matcher = new AnnotationValueMatcher; |
||
| 192 | $this->assertMatcherResult($matcher, '-3.14', -3.14); |
||
| 193 | $this->assertMatcherResult($matcher, '100', 100); |
||
| 194 | } |
||
| 195 | |||
| 196 | public function testValueMatcherShouldMatchArray() { |
||
| 197 | $matcher = new AnnotationValueMatcher; |
||
| 198 | $this->assertMatcherResult($matcher, '{1}', array(1)); |
||
| 199 | } |
||
| 200 | |||
| 201 | public function testValueMatcherShouldMatchStaticConstant() { |
||
| 202 | $matcher = new AnnotationValueMatcher; |
||
| 203 | $this->assertMatcherResult($matcher, 'StaticClass::A_CONSTANT', StaticClass::A_CONSTANT); |
||
| 204 | } |
||
| 205 | |||
| 206 | public function testValueMatcherShouldMatchAnnotation() { |
||
| 207 | $matcher = new AnnotationValueMatcher; |
||
| 208 | $this->assertNotIdentical($matcher->matches('@Annotation(true))', $value), false); |
||
| 209 | $this->assertIsA($value, 'Annotation'); |
||
| 210 | $this->assertTrue($value->value); |
||
| 211 | } |
||
| 212 | |||
| 213 | public function testArrayMatcherShouldMatchEmptyArray() { |
||
| 214 | $matcher = new AnnotationArrayMatcher; |
||
| 215 | $this->assertMatcherResult($matcher, '{}', array()); |
||
| 216 | } |
||
| 217 | |||
| 218 | public function testValueInArrayMatcherReturnsAValueInArray() { |
||
| 219 | $matcher = new AnnotationValueInArrayMatcher; |
||
| 220 | $this->assertMatcherResult($matcher, '1', array(1)); |
||
| 221 | } |
||
| 222 | |||
| 223 | public function testArrayMatcherShouldMatchSimpleValueInArray() { |
||
| 224 | $matcher = new AnnotationArrayMatcher; |
||
| 225 | $this->assertMatcherResult($matcher, '{1}', array(1)); |
||
| 226 | } |
||
| 227 | |||
| 228 | public function testArrayMatcherShouldMatchSimplePair() { |
||
| 229 | $matcher = new AnnotationArrayMatcher; |
||
| 230 | $this->assertMatcherResult($matcher, '{key=5}', array('key' => 5)); |
||
| 231 | } |
||
| 232 | |||
| 233 | public function TODO_testArrayMatcherShouldMatchPairWithNumericKeys() { |
||
| 234 | $matcher = new AnnotationArrayMatcher; |
||
| 235 | $this->assertMatcherResult($matcher, '{1="one", 2="two"}', array(1 => 'one', 2 => 'two')); |
||
| 236 | } |
||
| 237 | |||
| 238 | public function testArrayMatcherShouldMatchMultiplePairs() { |
||
| 239 | $matcher = new AnnotationArrayMatcher; |
||
| 240 | $this->assertMatcherResult($matcher, '{key=5, "bla"=false}', array('key' => 5, 'bla' => false)); |
||
| 241 | } |
||
| 242 | |||
| 243 | public function testArrayMatcherShouldMatchValuesMixedWithPairs() { |
||
| 244 | $matcher = new AnnotationArrayMatcher; |
||
| 245 | $this->assertMatcherResult($matcher, '{key=5, 1, 2, key2="ff"}', array('key' => 5, 1, 2, 'key2' => "ff")); |
||
| 246 | } |
||
| 247 | |||
| 248 | public function testArrayMatcherShouldMatchMoreValuesInArrayWithWhiteSpace() { |
||
| 249 | $matcher = new AnnotationArrayMatcher; |
||
| 250 | $this->assertMatcherResult($matcher, "{1 , 2}", array(1, 2)); |
||
| 251 | } |
||
| 252 | |||
| 253 | public function testArrayMatcherShouldMatchNestedArray() { |
||
| 254 | $matcher = new AnnotationArrayMatcher; |
||
| 255 | $this->assertMatcherResult($matcher, "{1 , {2, 3}, 4}", array(1, array(2, 3), 4)); |
||
| 256 | } |
||
| 257 | |||
| 258 | public function testArrayMatcherShouldMatchWithMoreWhiteSpace() { |
||
| 259 | $matcher = new AnnotationArrayMatcher; |
||
| 260 | $this->assertMatcherResult($matcher, "{ 1 , 2 , 3 }", array(1, 2, 3)); |
||
| 261 | } |
||
| 262 | |||
| 263 | public function testArrayMatcherShouldMatchWithMultilineWhiteSpace() { |
||
| 264 | $matcher = new AnnotationArrayMatcher; |
||
| 265 | $this->assertMatcherResult($matcher, "\n{1, 2, 3\n}", array(1, 2, 3)); |
||
| 266 | } |
||
| 267 | |||
| 268 | public function testNumberMatcherShouldMatchInteger() { |
||
| 269 | $matcher = new AnnotationNumberMatcher; |
||
| 270 | $this->assertMatcherResult($matcher, '-314', -314); |
||
| 271 | } |
||
| 272 | |||
| 273 | public function testNumberMatcherShouldMatchFloat() { |
||
| 274 | $matcher = new AnnotationNumberMatcher; |
||
| 275 | $this->assertMatcherResult($matcher, '-3.14', -3.14); |
||
| 276 | } |
||
| 277 | |||
| 278 | public function testHashMatcherShouldMatchSimpleHash() { |
||
| 279 | $matcher = new AnnotationHashMatcher; |
||
| 280 | $this->assertMatcherResult($matcher, 'key=true', array('key' => true)); |
||
| 281 | } |
||
| 282 | |||
| 283 | public function testHashMatcherShouldMatchAlsoMultipleKeys() { |
||
| 284 | $matcher = new AnnotationHashMatcher; |
||
| 285 | $this->assertMatcherResult($matcher, 'key=true,key2=false', array('key' => true, 'key2' => false)); |
||
| 286 | } |
||
| 287 | |||
| 288 | public function testHashMatcherShouldMatchAlsoMultipleKeysWithWhiteSpace() { |
||
| 289 | $matcher = new AnnotationHashMatcher; |
||
| 290 | $this->assertMatcherResult($matcher, "key=true\n\t\r ,\n\t\r key2=false", array('key' => true, 'key2' => false)); |
||
| 291 | } |
||
| 292 | |||
| 293 | public function testPairMatcherShouldMatchNumericKey() { |
||
| 294 | $matcher = new AnnotationPairMatcher; |
||
| 295 | $this->assertMatcherResult($matcher, '2 = true', array(2 => true)); |
||
| 296 | } |
||
| 297 | |||
| 298 | public function testPairMatcherShouldMatchAlsoWhitespace() { |
||
| 299 | $matcher = new AnnotationPairMatcher; |
||
| 300 | $this->assertMatcherResult($matcher, 'key = true', array('key' => true)); |
||
| 301 | } |
||
| 302 | |||
| 303 | public function testKeyMatcherShouldMatchSimpleKeysOrStrings() { |
||
| 304 | $matcher = new AnnotationKeyMatcher; |
||
| 305 | $this->assertNotFalse($matcher->matches('key', $value)); |
||
| 306 | $this->assertNotFalse($matcher->matches('"key"', $value)); |
||
| 307 | $this->assertNotFalse($matcher->matches("'key'", $value)); |
||
| 308 | } |
||
| 309 | |||
| 310 | public function testKeyMatcherShouldMatchIntegerKeys() { |
||
| 311 | $matcher = new AnnotationKeyMatcher; |
||
| 312 | $this->assertMatcherResult($matcher, '123', 123); |
||
| 313 | } |
||
| 314 | |||
| 315 | public function testStringMatcherShouldMatchDoubleAndSingleQuotedStringsAndHandleEscapes() { |
||
| 316 | $matcher = new AnnotationStringMatcher; |
||
| 317 | $this->assertMatcherResult($matcher, '"string string"', 'string string'); |
||
| 318 | $this->assertMatcherResult($matcher, "'string string'", "string string"); |
||
| 319 | } |
||
| 320 | |||
| 321 | public function TODO_testStringMatcherShouldMatchEscapedStringsCorrectly() { |
||
| 322 | $matcher = new AnnotationStringMatcher; |
||
| 323 | $this->assertMatcherResult($matcher, '"string\"string"', 'string"string'); |
||
| 324 | $this->assertMatcherResult($matcher, "'string\'string'", "string'string"); |
||
| 325 | } |
||
| 326 | |||
| 327 | public function testStaticConstantMatcherShouldMatchConstants() { |
||
| 328 | $matcher = new AnnotationStaticConstantMatcher; |
||
| 329 | $this->assertMatcherResult($matcher, 'StaticClass::A_CONSTANT', StaticClass::A_CONSTANT); |
||
| 330 | } |
||
| 331 | |||
| 332 | public function testStaticConstantMatcherShouldThrowErrorOnBadConstant() { |
||
| 333 | $this->expectError("Constant 'StaticClass::NO_CONSTANT' used in annotation was not defined."); |
||
| 334 | $matcher = new AnnotationStaticConstantMatcher; |
||
| 335 | $matcher->matches('StaticClass::NO_CONSTANT', $value); |
||
| 336 | } |
||
| 337 | |||
| 338 | public function testNestedAnnotationMatcherShouldMatchAnnotation() { |
||
| 339 | $matcher = new AnnotationValueMatcher; |
||
| 340 | $this->assertNotIdentical($matcher->matches('@Annotation(true))', $value), false); |
||
| 341 | $this->assertIsA($value, 'Annotation'); |
||
| 342 | $this->assertTrue($value->value); |
||
| 343 | } |
||
| 344 | |||
| 345 | |||
| 346 | private function assertNotFalse($value) { |
||
| 347 | $this->assertNotIdentical($value, false); |
||
| 348 | } |
||
| 349 | |||
| 350 | private function assertMatcherResult($matcher, $string, $expected) { |
||
| 351 | $this->assertNotIdentical($matcher->matches($string, $value), false); |
||
| 352 | $this->assertIdentical($value, $expected); |
||
| 353 | } |
||
| 354 | } |
||
| 355 | ?> |
||
| 356 |