Complex classes like WikibaseApiTestCase 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 WikibaseApiTestCase, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 25 | abstract class WikibaseApiTestCase extends ApiTestCase { |
||
| 26 | |||
| 27 | /** @var User */ |
||
| 28 | protected $user; |
||
| 29 | |||
| 30 | protected function setUp(): void { |
||
| 31 | |||
| 32 | parent::setUp(); |
||
| 33 | |||
| 34 | $this->setupUser(); |
||
| 35 | |||
| 36 | $this->setupSiteLinkGroups(); |
||
| 37 | |||
| 38 | $siteStore = new \HashSiteStore( TestSites::getSites() ); |
||
| 39 | $this->setService( 'SiteStore', $siteStore ); |
||
| 40 | $this->setService( 'SiteLookup', $siteStore ); |
||
| 41 | } |
||
| 42 | |||
| 43 | protected function createTestUser() { |
||
| 51 | |||
| 52 | private function setupUser() { |
||
| 69 | |||
| 70 | private function setupSiteLinkGroups() { |
||
| 71 | global $wgWBRepoSettings; |
||
| 72 | |||
| 73 | $customRepoSettings = $wgWBRepoSettings; |
||
| 74 | $customRepoSettings['siteLinkGroups'] = [ 'wikipedia' ]; |
||
| 75 | $this->setMwGlobals( 'wgWBRepoSettings', $customRepoSettings ); |
||
| 76 | MediaWikiServices::getInstance()->resetServiceForTesting( 'SiteLookup' ); |
||
| 77 | } |
||
| 78 | |||
| 79 | /** |
||
| 80 | * Appends an edit token to a request. |
||
| 81 | * |
||
| 82 | * @param array $params |
||
| 83 | * @param array|null $session |
||
| 84 | * @param User|null $user |
||
| 85 | * @param string $tokenType |
||
| 86 | * |
||
| 87 | * @throws ApiUsageException |
||
| 88 | * @return array( array|null $resultData, WebRequest $request, array $sessionArray ) |
||
|
|
|||
| 89 | */ |
||
| 90 | protected function doApiRequestWithToken( |
||
| 91 | array $params, |
||
| 92 | array $session = null, |
||
| 93 | User $user = null, |
||
| 94 | $tokenType = 'csrf' |
||
| 95 | ) { |
||
| 96 | if ( !$user ) { |
||
| 97 | $user = \RequestContext::getMain()->getUser(); |
||
| 98 | } |
||
| 99 | |||
| 100 | if ( !array_key_exists( 'token', $params ) ) { |
||
| 101 | $params['token'] = $user->getEditToken(); |
||
| 102 | } |
||
| 103 | |||
| 104 | return $this->doApiRequest( $params, $session, false, $user, $tokenType ); |
||
| 105 | } |
||
| 106 | |||
| 107 | /** |
||
| 108 | * @param string[] $handles |
||
| 109 | * @param string[] $idMap |
||
| 110 | */ |
||
| 111 | protected function initTestEntities( array $handles, array $idMap = [] ) { |
||
| 112 | $activeHandles = EntityTestHelper::getActiveHandles(); |
||
| 113 | $user = $this->getTestSysop()->getUser(); |
||
| 114 | |||
| 115 | foreach ( $activeHandles as $handle => $id ) { |
||
| 116 | $title = $this->getTestEntityTitle( $handle ); |
||
| 117 | |||
| 118 | $page = WikiPage::factory( $title ); |
||
| 119 | $page->doDeleteArticleReal( 'Test reset', $user ); |
||
| 120 | EntityTestHelper::unRegisterEntity( $handle ); |
||
| 121 | } |
||
| 122 | |||
| 123 | foreach ( $handles as $handle ) { |
||
| 124 | $params = EntityTestHelper::getEntity( $handle ); |
||
| 125 | $params['action'] = 'wbeditentity'; |
||
| 126 | |||
| 127 | EntityTestHelper::injectIds( $params, $idMap ); |
||
| 128 | EntityTestHelper::injectIds( $params, EntityTestHelper::$defaultPlaceholderValues ); |
||
| 129 | |||
| 130 | list( $res, , ) = $this->doApiRequestWithToken( $params ); |
||
| 131 | EntityTestHelper::registerEntity( $handle, $res['entity']['id'], $res['entity'] ); |
||
| 132 | |||
| 133 | $idMap["%$handle%"] = $res['entity']['id']; |
||
| 134 | } |
||
| 135 | } |
||
| 136 | |||
| 137 | /** |
||
| 138 | * @param string $handle |
||
| 139 | * |
||
| 140 | * @return null|Title |
||
| 141 | */ |
||
| 142 | protected function getTestEntityTitle( $handle ) { |
||
| 154 | |||
| 155 | /** |
||
| 156 | * Loads an entity from the database (via an API call). |
||
| 157 | * |
||
| 158 | * @param string $id |
||
| 159 | * |
||
| 160 | * @return array |
||
| 161 | */ |
||
| 162 | protected function loadEntity( $id ) { |
||
| 172 | |||
| 173 | /** |
||
| 174 | * Do the test for exceptions from Api queries. |
||
| 175 | * |
||
| 176 | * @param array $params Array of params for the API query. |
||
| 177 | * @param array $exception Details of the exception to expect (type, code, message, message-key). |
||
| 178 | * @param User|null $user |
||
| 179 | * @param bool $token Whether to include a CSRF token |
||
| 180 | */ |
||
| 181 | protected function doTestQueryExceptions( |
||
| 223 | |||
| 224 | /** |
||
| 225 | * Utility function for converting an array from "deep" (indexed) to "flat" (keyed) structure. |
||
| 226 | * Arrays that already use a flat structure are left unchanged. |
||
| 227 | * |
||
| 228 | * Arrays with a deep structure are expected to be list of entries that are associative arrays, |
||
| 229 | * where which entry has at least the fields given by $keyField and $valueField. |
||
| 230 | * |
||
| 231 | * Arrays with a flat structure are associative and assign values to meaningful keys. |
||
| 232 | * |
||
| 233 | * @param array $data the input array. |
||
| 234 | * @param string $keyField The name of the field in each entry that shall be used as the key in |
||
| 235 | * the flat structure. |
||
| 236 | * @param string $valueField The name of the field in each entry that shall be used as the value |
||
| 237 | * in the flat structure. |
||
| 238 | * @param bool $multiValue Whether the value in the flat structure shall be an indexed array of |
||
| 239 | * values instead of a single value. |
||
| 240 | * @param array &$into optional aggregator. |
||
| 241 | * |
||
| 242 | * @return array The flat version of $data. |
||
| 243 | */ |
||
| 244 | protected function flattenArray( array $data, $keyField, $valueField, $multiValue = false, array &$into = [] ) { |
||
| 279 | |||
| 280 | /** |
||
| 281 | * Compares two entity structures and asserts that they are equal. Only fields present in $expected are considered. |
||
| 282 | * $expected and $actual can both be either in "flat" or in "deep" form, they are converted as needed before comparison. |
||
| 283 | * |
||
| 284 | * @param array $expected |
||
| 285 | * @param array $actual |
||
| 286 | * @param bool $expectEmptyArrays Should we expect empty arrays or just ignore them? |
||
| 287 | */ |
||
| 288 | protected function assertEntityEquals( array $expected, array $actual, $expectEmptyArrays = true ) { |
||
| 289 | if ( isset( $expected['id'] ) && !empty( $expected['id'] ) ) { |
||
| 290 | $this->assertEquals( $expected['id'], $actual['id'], 'id' ); |
||
| 291 | } |
||
| 292 | if ( isset( $expected['lastrevid'] ) ) { |
||
| 293 | $this->assertEquals( $expected['lastrevid'], $actual['lastrevid'], 'lastrevid' ); |
||
| 294 | } |
||
| 295 | if ( isset( $expected['type'] ) ) { |
||
| 296 | $this->assertEquals( $expected['type'], $actual['type'], 'type' ); |
||
| 297 | } |
||
| 298 | |||
| 299 | if ( isset( $expected['labels'] ) ) { |
||
| 300 | if ( !( $expectEmptyArrays === false && $expected['labels'] === [] ) ) { |
||
| 301 | $data = $this->flattenArray( $actual['labels'], 'language', 'value' ); |
||
| 302 | $exp = $this->flattenArray( $expected['labels'], 'language', 'value' ); |
||
| 303 | |||
| 304 | // keys are significant in flat form |
||
| 305 | $this->assertArrayEquals( $exp, $data, false, true ); |
||
| 306 | } |
||
| 307 | } |
||
| 308 | |||
| 309 | if ( isset( $expected['descriptions'] ) ) { |
||
| 310 | if ( !( $expectEmptyArrays === false && $expected['descriptions'] === [] ) ) { |
||
| 311 | $data = $this->flattenArray( $actual['descriptions'], 'language', 'value' ); |
||
| 312 | $exp = $this->flattenArray( $expected['descriptions'], 'language', 'value' ); |
||
| 313 | |||
| 314 | // keys are significant in flat form |
||
| 315 | $this->assertArrayEquals( $exp, $data, false, true ); |
||
| 316 | } |
||
| 317 | } |
||
| 318 | |||
| 319 | if ( isset( $expected['sitelinks'] ) ) { |
||
| 320 | if ( !( $expectEmptyArrays === false && $expected['sitelinks'] === [] ) ) { |
||
| 321 | $data = $this->flattenArray( $actual['sitelinks'] ?? [], 'site', 'title' ); |
||
| 322 | $exp = $this->flattenArray( $expected['sitelinks'], 'site', 'title' ); |
||
| 323 | |||
| 324 | // keys are significant in flat form |
||
| 325 | $this->assertArrayEquals( $exp, $data, false, true ); |
||
| 326 | } |
||
| 327 | } |
||
| 328 | |||
| 329 | if ( isset( $expected['aliases'] ) ) { |
||
| 330 | if ( !( $expectEmptyArrays === false && $expected['aliases'] === [] ) ) { |
||
| 331 | $data = $this->flattenArray( $actual['aliases'], 'language', 'value', true ); |
||
| 332 | $exp = $this->flattenArray( $expected['aliases'], 'language', 'value', true ); |
||
| 333 | |||
| 334 | // keys are significant in flat form |
||
| 335 | $this->assertArrayEquals( $exp, $data, false, true ); |
||
| 336 | } |
||
| 337 | } |
||
| 338 | |||
| 339 | if ( isset( $expected['claims'] ) ) { |
||
| 340 | if ( !( $expectEmptyArrays === false && $expected['claims'] === [] ) ) { |
||
| 341 | $data = $this->flattenArray( $actual['claims'], 'mainsnak', 'value', true ); |
||
| 342 | $exp = $this->flattenArray( $expected['claims'], 'language', 'value', true ); |
||
| 343 | $count = count( $expected['claims'] ); |
||
| 344 | |||
| 345 | for ( $i = 0; $i < $count; $i++ ) { |
||
| 346 | $this->assertArrayHasKey( $i, $data['id'] ); |
||
| 347 | $this->assertGreaterThanOrEqual( 39, strlen( $data['id'][$i] ) ); |
||
| 348 | } |
||
| 349 | //unset stuff we dont actually want to compare |
||
| 350 | if ( isset( $exp['id'] ) ) { |
||
| 351 | $this->assertArrayHasKey( 'id', $data ); |
||
| 352 | } |
||
| 353 | unset( $exp['id'] ); |
||
| 354 | unset( $exp['datatype'] ); |
||
| 355 | unset( $exp['hash'] ); |
||
| 356 | unset( $exp['qualifiers-order'] ); |
||
| 357 | unset( $data['datatype'] ); |
||
| 358 | unset( $data['id'] ); |
||
| 359 | unset( $data['hash'] ); |
||
| 360 | unset( $data['qualifiers-order'] ); |
||
| 361 | $this->assertArrayEquals( $exp, $data, false, true ); |
||
| 362 | } |
||
| 363 | } |
||
| 364 | } |
||
| 365 | |||
| 366 | /** |
||
| 367 | * Asserts that the given API response represents a successful call. |
||
| 368 | * |
||
| 369 | * @param array $response |
||
| 370 | */ |
||
| 371 | protected function assertResultSuccess( array $response ) { |
||
| 375 | |||
| 376 | /** |
||
| 377 | * Asserts that the given API response has a valid entity type if the result contains an entity |
||
| 378 | * |
||
| 379 | * @param array $response |
||
| 380 | */ |
||
| 381 | protected function assertResultHasEntityType( array $response ) { |
||
| 404 | |||
| 405 | /** |
||
| 406 | * Asserts that the revision with the given ID has a summary matching $regex |
||
| 407 | * |
||
| 408 | * @param string|string[] $regex The regex to match, or an array to build a regex from. |
||
| 409 | * @param int $revid |
||
| 410 | */ |
||
| 411 | protected function assertRevisionSummary( $regex, $revid ) { |
||
| 435 | |||
| 436 | protected function assertCanTagSuccessfulRequest( |
||
| 464 | |||
| 465 | private function getLastRevIdFromResult( array $result ) { |
||
| 475 | |||
| 476 | } |
||
| 477 |
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.