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 BaseTest 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 BaseTest, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
33 | abstract class BaseTest extends TestCase |
||
34 | { |
||
35 | /** |
||
36 | * Maximum integer number accepted by the different backends. |
||
37 | */ |
||
38 | const DB_INT_MAX = 2147483647; |
||
39 | |||
40 | /** |
||
41 | * @var \eZ\Publish\API\Repository\Tests\SetupFactory |
||
42 | */ |
||
43 | private $setupFactory; |
||
44 | |||
45 | /** |
||
46 | * @var \eZ\Publish\API\Repository\Repository |
||
47 | */ |
||
48 | private $repository; |
||
49 | |||
50 | protected function setUp() |
||
79 | |||
80 | /** |
||
81 | * Resets the temporary used repository between each test run. |
||
82 | */ |
||
83 | protected function tearDown() |
||
88 | |||
89 | /** |
||
90 | * Returns the ID generator, fitting to the repository implementation. |
||
91 | * |
||
92 | * @return \eZ\Publish\API\Repository\Tests\IdManager |
||
93 | */ |
||
94 | protected function getIdManager() |
||
98 | |||
99 | /** |
||
100 | * Generates a repository specific ID value. |
||
101 | * |
||
102 | * @param string $type |
||
103 | * @param mixed $rawId |
||
104 | * |
||
105 | * @return mixed |
||
106 | */ |
||
107 | protected function generateId($type, $rawId) |
||
111 | |||
112 | /** |
||
113 | * Parses a repository specific ID value. |
||
114 | * |
||
115 | * @param string $type |
||
116 | * @param mixed $id |
||
117 | * |
||
118 | * @return mixed |
||
119 | */ |
||
120 | protected function parseId($type, $id) |
||
124 | |||
125 | /** |
||
126 | * Returns a config setting provided by the setup factory. |
||
127 | * |
||
128 | * @param string $configKey |
||
129 | * |
||
130 | * @return mixed |
||
131 | */ |
||
132 | protected function getConfigValue($configKey) |
||
136 | |||
137 | /** |
||
138 | * Tests if the currently tested api is based on a V4 implementation. |
||
139 | * |
||
140 | * @return bool |
||
141 | */ |
||
142 | protected function isVersion4() |
||
146 | |||
147 | /** |
||
148 | * @param bool $initialInitializeFromScratch Only has an effect if set in first call within a test |
||
149 | * |
||
150 | * @return \eZ\Publish\API\Repository\Repository |
||
151 | */ |
||
152 | protected function getRepository($initialInitializeFromScratch = true) |
||
160 | |||
161 | /** |
||
162 | * @return \eZ\Publish\API\Repository\Tests\SetupFactory |
||
163 | */ |
||
164 | protected function getSetupFactory() |
||
190 | |||
191 | /** |
||
192 | * Asserts that properties given in $expectedValues are correctly set in |
||
193 | * $actualObject. |
||
194 | * |
||
195 | * @param mixed[] $expectedValues |
||
196 | * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject |
||
197 | */ |
||
198 | protected function assertPropertiesCorrect(array $expectedValues, ValueObject $actualObject) |
||
216 | |||
217 | /** |
||
218 | * Asserts that properties given in $expectedValues are correctly set in |
||
219 | * $actualObject. |
||
220 | * |
||
221 | * If the property type is array, it will be sorted before comparison. |
||
222 | * |
||
223 | * @TODO: introduced because of randomly failing tests, ref: https://jira.ez.no/browse/EZP-21734 |
||
224 | * |
||
225 | * @param mixed[] $expectedValues |
||
226 | * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject |
||
227 | */ |
||
228 | protected function assertPropertiesCorrectUnsorted(array $expectedValues, ValueObject $actualObject) |
||
238 | |||
239 | /** |
||
240 | * Asserts all properties from $expectedValues are correctly set in |
||
241 | * $actualObject. Additional (virtual) properties can be asserted using |
||
242 | * $additionalProperties. |
||
243 | * |
||
244 | * @param \eZ\Publish\API\Repository\Values\ValueObject $expectedValues |
||
245 | * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject |
||
246 | * @param array $propertyNames |
||
247 | */ |
||
248 | protected function assertStructPropertiesCorrect(ValueObject $expectedValues, ValueObject $actualObject, array $additionalProperties = array()) |
||
262 | |||
263 | /** |
||
264 | * @see \eZ\Publish\API\Repository\Tests\BaseTest::assertPropertiesCorrectUnsorted() |
||
265 | * |
||
266 | * @param array $items An array of scalar values |
||
267 | */ |
||
268 | private function sortItems(array &$items) |
||
279 | |||
280 | private function assertPropertiesEqual($propertyName, $expectedValue, $actualValue, $sortArray = false) |
||
304 | |||
305 | /** |
||
306 | * Create a user in editor user group. |
||
307 | * |
||
308 | * @param string $login |
||
309 | * |
||
310 | * @return \eZ\Publish\API\Repository\Values\User\User |
||
311 | */ |
||
312 | protected function createUserVersion1($login = 'user') |
||
344 | |||
345 | /** |
||
346 | * Create a user in new user group with editor rights limited to Media Library (/1/48/). |
||
347 | * |
||
348 | * @uses ::createCustomUserVersion1() |
||
349 | * |
||
350 | * @return \eZ\Publish\API\Repository\Values\User\User |
||
351 | */ |
||
352 | protected function createMediaUserVersion1() |
||
360 | |||
361 | /** |
||
362 | * Create a user with new user group and assign a existing role (optionally with RoleLimitation). |
||
363 | * |
||
364 | * @param string $userGroupName Name of the new user group to create |
||
365 | * @param string $roleIdentifier Role identifier to assign to the new group |
||
366 | * @param RoleLimitation|null $roleLimitation |
||
367 | * |
||
368 | * @return \eZ\Publish\API\Repository\Values\User\User |
||
369 | */ |
||
370 | protected function createCustomUserVersion1($userGroupName, $roleIdentifier, RoleLimitation $roleLimitation = null) |
||
380 | |||
381 | /** |
||
382 | * Create a user with new user group and assign a existing role (optionally with RoleLimitation). |
||
383 | * |
||
384 | * @param string $login User login |
||
385 | * @param string $email User e-mail |
||
386 | * @param string $userGroupName Name of the new user group to create |
||
387 | * @param string $roleIdentifier Role identifier to assign to the new group |
||
388 | * @param RoleLimitation|null $roleLimitation |
||
389 | * @return \eZ\Publish\API\Repository\Values\User\User |
||
390 | */ |
||
391 | protected function createCustomUserWithLogin( |
||
441 | |||
442 | /** |
||
443 | * Create a user using given data. |
||
444 | * |
||
445 | * @param string $login |
||
446 | * @param string $firstName |
||
447 | * @param string $lastName |
||
448 | * @param \eZ\Publish\API\Repository\Values\User\UserGroup|null $userGroup optional user group, Editor by default |
||
449 | * |
||
450 | * @return \eZ\Publish\API\Repository\Values\User\User |
||
451 | */ |
||
452 | protected function createUser($login, $firstName, $lastName, UserGroup $userGroup = null) |
||
479 | |||
480 | /** |
||
481 | * Only for internal use. |
||
482 | * |
||
483 | * Creates a \DateTime object for $timestamp in the current time zone |
||
484 | * |
||
485 | * @param int $timestamp |
||
486 | * |
||
487 | * @return \DateTime |
||
488 | */ |
||
489 | public function createDateTime($timestamp = null) |
||
498 | |||
499 | /** |
||
500 | * Calls given Repository's aggregated SearchHandler::refresh(). |
||
501 | * |
||
502 | * Currently implemented only in Solr search engine. |
||
503 | * |
||
504 | * @param \eZ\Publish\API\Repository\Repository $repository |
||
505 | */ |
||
506 | protected function refreshSearch(Repository $repository) |
||
534 | |||
535 | /** |
||
536 | * Create role of a given name with the given policies described by an array. |
||
537 | * |
||
538 | * @param $roleName |
||
539 | * @param array $policiesData [['module' => 'content', 'function' => 'read', 'limitations' => []] |
||
540 | * |
||
541 | * @return \eZ\Publish\API\Repository\Values\User\Role |
||
542 | * |
||
543 | * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException |
||
544 | * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException |
||
545 | * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException |
||
546 | * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException |
||
547 | */ |
||
548 | public function createRoleWithPolicies($roleName, array $policiesData) |
||
575 | |||
576 | /** |
||
577 | * Create user and assign new role with the given policies. |
||
578 | * |
||
579 | * @param string $login |
||
580 | * @param array $policiesData list of policies in the form of <code>[ [ 'module' => 'name', 'function' => 'name'] ]</code> |
||
581 | * |
||
582 | * @return \eZ\Publish\API\Repository\Values\User\User |
||
583 | * |
||
584 | * @throws \Exception |
||
585 | */ |
||
586 | public function createUserWithPolicies($login, array $policiesData) |
||
615 | |||
616 | /** |
||
617 | * @return \Doctrine\DBAL\Connection |
||
618 | * |
||
619 | * @throws \ErrorException |
||
620 | */ |
||
621 | protected function getRawDatabaseConnection() |
||
635 | |||
636 | /** |
||
637 | * Executes the given callback passing raw Database Connection (\Doctrine\DBAL\Connection). |
||
638 | * Returns the result returned by the given callback. |
||
639 | * |
||
640 | * **Note**: The method clears the entire persistence cache pool. |
||
641 | * |
||
642 | * @throws \Exception |
||
643 | * |
||
644 | * @param callable $callback |
||
645 | * |
||
646 | * @return mixed the return result of the given callback |
||
647 | */ |
||
648 | public function performRawDatabaseOperation(callable $callback) |
||
669 | |||
670 | /** |
||
671 | * Traverse all errors for all fields in all Translations to find expected one. |
||
672 | * |
||
673 | * @param \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $exception |
||
674 | * @param string $expectedValidationErrorMessage |
||
675 | */ |
||
676 | protected function assertValidationErrorOccurs( |
||
684 | |||
685 | /** |
||
686 | * Create 'folder' Content. |
||
687 | * |
||
688 | * @param array $names Folder names in the form of <code>['<language_code>' => '<name>']</code> |
||
689 | * @param int $parentLocationId |
||
690 | * |
||
691 | * @return \eZ\Publish\API\Repository\Values\Content\Content published Content |
||
692 | * |
||
693 | * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException |
||
694 | * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException |
||
695 | * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException |
||
696 | */ |
||
697 | protected function createFolder(array $names, $parentLocationId) |
||
723 | |||
724 | /** |
||
725 | * Update 'folder' Content. |
||
726 | * |
||
727 | * @param \eZ\Publish\API\Repository\Values\Content\Content $content |
||
728 | * @param array $names Folder names in the form of <code>['<language_code>' => '<name>']</code> |
||
729 | * |
||
730 | * @return \eZ\Publish\API\Repository\Values\Content\Content |
||
731 | * |
||
732 | * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException |
||
733 | * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException |
||
734 | * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException |
||
735 | * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException |
||
736 | * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException |
||
737 | */ |
||
738 | protected function updateFolder(Content $content, array $names) |
||
753 | |||
754 | /** |
||
755 | * Add new Language to the Repository. |
||
756 | * |
||
757 | * @param string $languageCode |
||
758 | * @param string $name |
||
759 | * @param bool $enabled |
||
760 | * |
||
761 | * @return \eZ\Publish\API\Repository\Values\Content\Language |
||
762 | */ |
||
763 | protected function createLanguage(string $languageCode, string $name, bool $enabled = true): Language |
||
775 | } |
||
776 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.