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:
| 1 | <?php |
||
| 6 | class ElasticsearchBaseTest extends SapphireTest { |
||
| 7 | |||
| 8 | public static $ignoreFixtureFileFor = array(); |
||
| 9 | |||
| 10 | protected $extraDataObjects = array( |
||
| 11 | 'SearchableTestPage','FlickrPhotoTO','FlickrAuthorTO','FlickrSetTO','FlickrTagTO', |
||
| 12 | 'SearchableTestFatherPage','SearchableTestGrandFatherPage','AutoCompleteOption' |
||
| 13 | ); |
||
| 14 | |||
| 15 | |||
| 16 | public function setUpOnce() { |
||
| 33 | |||
| 34 | |||
| 35 | 2 | public function setUp() { |
|
|
|
|||
| 36 | // no need to index here as it's done when fixtures are loaded during setup method |
||
| 37 | 2 | $cache = SS_Cache::factory('elasticsearch'); |
|
| 38 | 2 | $cache->clean(Zend_Cache::CLEANING_MODE_ALL); |
|
| 39 | 2 | SS_Cache::set_cache_lifetime('elasticsearch', 3600, 1000); |
|
| 40 | |||
| 41 | // this needs to be called in order to create the list of searchable |
||
| 42 | // classes and fields that are available. Simulates part of a build |
||
| 43 | 2 | $classes = array('SearchableTestPage','SiteTree','Page','FlickrPhotoTO','FlickrSetTO', |
|
| 44 | 2 | 'FlickrTagTO', 'FlickrAuthorTO'); |
|
| 45 | 2 | $this->requireDefaultRecordsFrom = $classes; |
|
| 46 | |||
| 47 | |||
| 48 | // clear the index |
||
| 49 | 2 | $this->service = Injector::inst()->create('SilverStripe\Elastica\ElasticaService'); |
|
| 50 | 2 | $this->service->setTestMode(true); |
|
| 51 | |||
| 52 | // A previous test may have deleted the index and then failed, so check for this |
||
| 53 | 2 | if (!$this->service->getIndex()->exists()) { |
|
| 54 | 1 | $this->service->getIndex()->create(); |
|
| 55 | 1 | } |
|
| 56 | 2 | $this->service->reset(); |
|
| 57 | |||
| 58 | // FIXME - use request getVar instead? |
||
| 59 | 2 | $_GET['progress'] = 20; |
|
| 60 | // load fixtures |
||
| 61 | |||
| 62 | 2 | $orig_fixture_file = static::$fixture_file; |
|
| 63 | |||
| 64 | 2 | View Code Duplication | foreach (static::$ignoreFixtureFileFor as $testPattern) { |
| 65 | $pattern = '/'.$testPattern.'/'; |
||
| 66 | 1 | if (preg_match($pattern, $this->getName())) { |
|
| 67 | static::$fixture_file = null; |
||
| 68 | } |
||
| 69 | 2 | } |
|
| 70 | |||
| 71 | 2 | parent::setUp(); |
|
| 72 | 2 | static::$fixture_file = $orig_fixture_file; |
|
| 73 | |||
| 74 | 2 | $this->publishSiteTree(); |
|
| 75 | |||
| 76 | 2 | $this->service->reset(); |
|
| 77 | |||
| 78 | // index loaded fixtures |
||
| 79 | 2 | $task = new ReindexTask($this->service); |
|
| 80 | // null request is fine as no parameters used |
||
| 81 | |||
| 82 | 2 | $task->run(null); |
|
| 83 | |||
| 84 | 2 | } |
|
| 85 | |||
| 86 | |||
| 87 | protected function devBuild() { |
||
| 92 | |||
| 93 | |||
| 94 | 2 | private function publishSiteTree() { |
|
| 95 | 2 | foreach (SiteTree::get()->getIterator() as $page) { |
|
| 96 | // temporarily disable Elasticsearch indexing, it will be done in a batch |
||
| 97 | $page->IndexingOff = true; |
||
| 98 | $page->publish('Stage','Live'); |
||
| 99 | 2 | } |
|
| 100 | 2 | } |
|
| 101 | |||
| 102 | |||
| 103 | View Code Duplication | public function generateAssertionsFromArray($toAssert) { |
|
| 112 | |||
| 113 | |||
| 114 | View Code Duplication | public function generateAssertionsFromArray1D($toAssert) { |
|
| 123 | |||
| 124 | |||
| 125 | public function generateAssertionsFromArrayRecurse($toAssert) { |
||
| 130 | |||
| 131 | |||
| 132 | private function recurseArrayAssertion($toAssert, $depth, $parentKey) { |
||
| 155 | |||
| 156 | |||
| 157 | /* |
||
| 158 | Helper methods for testing CMS fields |
||
| 159 | */ |
||
| 160 | 1 | public function checkTabExists($fields, $tabName) { |
|
| 161 | 1 | $tab = $fields->findOrMakeTab("Root.{$tabName}"); |
|
| 162 | 1 | $actualTabName = $tab->getName(); |
|
| 163 | 1 | $splits = explode('.', $tabName); |
|
| 164 | 1 | $size = sizeof($splits); |
|
| 165 | 1 | $nameToCheck = end($splits); |
|
| 166 | 1 | $this->assertEquals($actualTabName, $nameToCheck); |
|
| 167 | 1 | if ($size == 1) { |
|
| 168 | 1 | $this->assertEquals("Root_${tabName}", $tab->id()); |
|
| 169 | 1 | } else { |
|
| 170 | $expected = "Root_{$splits[0]}_set_{$splits[1]}"; |
||
| 171 | $this->assertEquals($expected, $tab->id()); |
||
| 172 | } |
||
| 173 | |||
| 174 | return $tab; |
||
| 175 | } |
||
| 176 | |||
| 177 | |||
| 178 | public function checkFieldExists($tab,$fieldName) { |
||
| 179 | 1 | $fields = $tab->Fields(); |
|
| 180 | 1 | $field = $tab->fieldByName($fieldName); |
|
| 181 | 1 | $this->assertTrue($field != null); |
|
| 182 | 1 | return $field; |
|
| 183 | } |
||
| 184 | |||
| 185 | |||
| 186 | /** |
||
| 187 | * From https://jtreminio.com/2013/03/unit-testing-tutorial-part-3-testing-protected-private-methods-coverage-reports-and-crap/ |
||
| 188 | * Call protected/private method of a class. |
||
| 189 | * |
||
| 190 | * @param object &$object Instantiated object that we will run method on. |
||
| 191 | * @param string $methodName Method name to call |
||
| 192 | * @param array $parameters Array of parameters to pass into method. |
||
| 193 | * |
||
| 194 | * @return mixed Method return. |
||
| 195 | */ |
||
| 196 | public function invokeMethod(&$object, $methodName, array $parameters = array()) |
||
| 204 | |||
| 205 | |||
| 206 | View Code Duplication | public function checkNumberOfIndexedDocuments($expectedAmount) { |
|
| 217 | |||
| 218 | /* |
||
| 219 | Get the number of documents in an index. It is assumed the index exists, if not the test will fail |
||
| 220 | */ |
||
| 221 | View Code Duplication | public function getNumberOfIndexedDocuments() { |
|
| 233 | } |
||
| 234 |
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: