Complex classes like ApiContext 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 ApiContext, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
9 | class ApiContext extends BaseFeatureContext |
||
10 | { |
||
11 | /** |
||
12 | * @var string |
||
13 | */ |
||
14 | protected static $authToken; |
||
15 | |||
16 | /** |
||
17 | * @var string |
||
18 | */ |
||
19 | protected static $userToken; |
||
20 | |||
21 | /** |
||
22 | * @BeforeScenario |
||
23 | */ |
||
24 | public function onBefore() |
||
30 | |||
31 | /** |
||
32 | * @When I get/GET to :path |
||
33 | */ |
||
34 | public function iGetTo($path) |
||
38 | |||
39 | /** |
||
40 | * @When I request the api with :method :path |
||
41 | */ |
||
42 | public function iRequestTheApi($method, $path) |
||
46 | |||
47 | /** |
||
48 | * @When I head/HEAD to :path |
||
49 | */ |
||
50 | public function iHeadTo($path) |
||
54 | |||
55 | /** |
||
56 | * @When I post/POST to :path with: |
||
57 | */ |
||
58 | public function iPostToWith($path, PyStringNode $string) |
||
62 | |||
63 | /** |
||
64 | * @When I put/PUT to :path with: |
||
65 | */ |
||
66 | public function iPutToWith($path, PyStringNode $string) |
||
70 | |||
71 | /** |
||
72 | * @When I delete/DELETE to :path |
||
73 | */ |
||
74 | public function iDeleteTo($path) |
||
78 | |||
79 | /** |
||
80 | * @Then the response should not contain :content |
||
81 | */ |
||
82 | public function theResponseShouldNotContain($content) |
||
86 | |||
87 | /** |
||
88 | * @Then the response status code should be :code |
||
89 | */ |
||
90 | public function theResponseStatusCodeShouldBe($code) |
||
94 | |||
95 | /** |
||
96 | * @Then the response header :header should contain :value |
||
97 | * @Then the response header :header contains :value |
||
98 | */ |
||
99 | public function theResponseHeaderContains($header, $value) |
||
109 | |||
110 | /** |
||
111 | * @Then the response should contain :arg1 |
||
112 | * @Then the response contains :arg1 |
||
113 | */ |
||
114 | public function theResponseShouldContain($arg1) |
||
118 | |||
119 | /** |
||
120 | * @Then the response should be xml/XML |
||
121 | * @Then the response is xml/XML |
||
122 | */ |
||
123 | public function theResponseIsXml() |
||
136 | |||
137 | /** |
||
138 | * @Then the response should be json/JSON |
||
139 | * @Then the response is json/JSON |
||
140 | */ |
||
141 | public function theResponseIsJson() |
||
153 | |||
154 | /** |
||
155 | * @Then the response should contain the following json/JSON: |
||
156 | * @Then the response contains the following json/JSON: |
||
157 | */ |
||
158 | public function theResponseShouldContainJson($jsonData) |
||
165 | |||
166 | /** |
||
167 | * @Then the response should contain the following jsonp/JSONP: |
||
168 | * @Then the response contains the following jsonp/JSONP: |
||
169 | */ |
||
170 | public function theResponseShouldContainJsonp(PyStringNode $jsonData) |
||
177 | |||
178 | /** |
||
179 | * @Given I have a valid token |
||
180 | */ |
||
181 | public function iHaveAValidToken() |
||
185 | |||
186 | /** |
||
187 | * @Given I have a valid token for :user with password :pass |
||
188 | * @Given I have a valid token for username :user and password :pass |
||
189 | */ |
||
190 | public function iHaveAValidTokenForUsernameAndPassword($user, $pass) |
||
210 | |||
211 | /** |
||
212 | * @Given I have a valid user token |
||
213 | */ |
||
214 | public function iHaveAValidUserToken() |
||
218 | |||
219 | /** @Given I have a valid user token for username :user and password :pass |
||
220 | */ |
||
221 | public function iHaveAValidUserTokenForUsernameAndPassword($user, $pass) |
||
233 | |||
234 | /** |
||
235 | * @Then the api/API result should be ok |
||
236 | */ |
||
237 | public function theResultShouldBeOk() |
||
245 | |||
246 | /** |
||
247 | * @Then the api/API result should be not ok |
||
248 | */ |
||
249 | public function theResultShouldBeNotOk() |
||
257 | |||
258 | /** |
||
259 | * @Then the api/API result should contain :count item(s) |
||
260 | * @Then the api/API result contains :count item(s) |
||
261 | */ |
||
262 | public function theResultContainsExactlyItems($count) |
||
269 | |||
270 | /** |
||
271 | * @Then the api/API result should contain at least :count item(s) |
||
272 | * @Then the api/API result contains at least :count item(s) |
||
273 | */ |
||
274 | public function theResultContainsAtLeastItems($count) |
||
281 | |||
282 | /** |
||
283 | * @Then the api/API result should contain key :key |
||
284 | */ |
||
285 | public function theResultShouldContainKey($key) |
||
290 | |||
291 | /** |
||
292 | * @Then the api/API result key :key should not equal null |
||
293 | */ |
||
294 | public function theResultKeyShouldNotEqualNull($key) |
||
300 | |||
301 | /** |
||
302 | * @Then the api/API result key :key should equal :value |
||
303 | */ |
||
304 | public function theResultKeyShouldEqual($key, $value) |
||
310 | |||
311 | /** |
||
312 | * @Then the api/API result key :key should equal: |
||
313 | */ |
||
314 | public function theResultKeyShouldEqualNode($key, $node) |
||
322 | |||
323 | /** |
||
324 | * @Then the api/API error should contain :text |
||
325 | */ |
||
326 | public function theApiErrorShouldContain($text) |
||
331 | |||
332 | /** |
||
333 | * @Then the api/API error should be: |
||
334 | */ |
||
335 | public function theApiErrorShouldEqual(PyStringNode $node) |
||
336 | { |
||
337 | $error = $this->getApiError(); |
||
338 | Assert::assertEquals($node->getRaw(), trim($error)); |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * @Then the api/API result should contain a key with: |
||
343 | */ |
||
344 | public function theResultShouldContainNode($node) |
||
357 | |||
358 | /** |
||
359 | * @param string|PyStringNode|TableNode $data |
||
360 | * |
||
361 | * @throws \InvalidArgumentException |
||
362 | * |
||
363 | * @return array |
||
364 | */ |
||
365 | protected function getJsonData($data) |
||
390 | |||
391 | /** |
||
392 | * @inheritdoc |
||
393 | */ |
||
394 | protected function request($method, $uri, $data = null, array $headers = [], array $server = []) |
||
410 | |||
411 | /** |
||
412 | * Returns the complete response from the last api call, decoded to an array. |
||
413 | * |
||
414 | * @return array |
||
415 | */ |
||
416 | protected function getApiResponse() |
||
420 | |||
421 | /** |
||
422 | * Returns the "result" key of the last api response, json decoded. |
||
423 | * |
||
424 | * @return mixed |
||
425 | */ |
||
426 | protected function getApiResult() |
||
434 | |||
435 | /** |
||
436 | * Returns the "error" key of the last api response. |
||
437 | * |
||
438 | * @return mixed |
||
439 | */ |
||
440 | protected function getApiError() |
||
448 | } |
||
449 |
This check looks for a call to a parent method whose name is different than the method from which it is called.
Consider the following code:
The
getFirstName()
method in theSon
calls the wrong method in the parent class.