Complex classes like BrowserConfiguration 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 BrowserConfiguration, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
38 | class BrowserConfiguration implements EventSubscriberInterface |
||
39 | { |
||
40 | const TYPE = 'default'; |
||
41 | |||
42 | /** |
||
43 | * User defaults. |
||
44 | * |
||
45 | * @var array |
||
46 | */ |
||
47 | protected $defaults = array( |
||
48 | // Driver related. |
||
49 | 'host' => 'localhost', |
||
50 | 'driver' => 'selenium2', |
||
51 | 'driverOptions' => array(), |
||
52 | |||
53 | // TODO: Move under 'driverOptions' of 'selenium2' driver (BC break). |
||
54 | 'desiredCapabilities' => array(), |
||
55 | 'timeout' => 60, |
||
56 | |||
57 | // Browser related. |
||
58 | 'browserName' => 'firefox', // Have no effect on headless drivers. |
||
59 | 'baseUrl' => '', |
||
60 | |||
61 | // Test related. |
||
62 | 'sessionStrategy' => ISessionStrategyFactory::TYPE_ISOLATED, |
||
63 | ); |
||
64 | |||
65 | /** |
||
66 | * User defaults merged with driver defaults. |
||
67 | * |
||
68 | * @var array |
||
69 | */ |
||
70 | private $_mergedDefaults = array(); |
||
71 | |||
72 | /** |
||
73 | * Manually set browser configuration parameters. |
||
74 | * |
||
75 | * @var array |
||
76 | */ |
||
77 | private $_parameters = array(); |
||
78 | |||
79 | /** |
||
80 | * Browser configuration aliases. |
||
81 | * |
||
82 | * @var array |
||
83 | */ |
||
84 | protected $aliases; |
||
85 | |||
86 | /** |
||
87 | * Test case. |
||
88 | * |
||
89 | * @var BrowserTestCase |
||
90 | */ |
||
91 | private $_testCase; |
||
92 | |||
93 | /** |
||
94 | * Event dispatcher. |
||
95 | * |
||
96 | * @var EventDispatcherInterface |
||
97 | */ |
||
98 | private $_eventDispatcher; |
||
99 | |||
100 | /** |
||
101 | * Driver factory registry. |
||
102 | * |
||
103 | * @var DriverFactoryRegistry |
||
104 | */ |
||
105 | private $_driverFactoryRegistry; |
||
106 | |||
107 | /** |
||
108 | * Driver factory. |
||
109 | * |
||
110 | * @var IMinkDriverFactory |
||
111 | */ |
||
112 | private $_driverFactory; |
||
113 | |||
114 | /** |
||
115 | * Resolves browser alias into corresponding browser configuration. |
||
116 | * |
||
117 | * @param array $parameters Browser configuration. |
||
118 | * @param array $aliases Browser configuration aliases. |
||
119 | * |
||
120 | * @return array |
||
121 | * @throws \InvalidArgumentException When unable to resolve used browser alias. |
||
122 | */ |
||
123 | 34 | public static function resolveAliases(array $parameters, array $aliases) |
|
124 | { |
||
125 | 34 | if ( !isset($parameters['alias']) ) { |
|
126 | 31 | return $parameters; |
|
127 | } |
||
128 | |||
129 | 16 | $browser_alias = $parameters['alias']; |
|
130 | 16 | unset($parameters['alias']); |
|
131 | |||
132 | 16 | if ( isset($aliases[$browser_alias]) ) { |
|
133 | 13 | $candidate_params = self::arrayMergeRecursive($aliases[$browser_alias], $parameters); |
|
134 | |||
135 | 13 | return self::resolveAliases($candidate_params, $aliases); |
|
136 | } |
||
137 | |||
138 | 3 | throw new \InvalidArgumentException(sprintf('Unable to resolve "%s" browser alias', $browser_alias)); |
|
139 | } |
||
140 | |||
141 | /** |
||
142 | * Creates browser configuration. |
||
143 | * |
||
144 | * @param EventDispatcherInterface $event_dispatcher Event dispatcher. |
||
145 | * @param DriverFactoryRegistry $driver_factory_registry Driver factory registry. |
||
146 | */ |
||
147 | 156 | public function __construct( |
|
148 | EventDispatcherInterface $event_dispatcher, |
||
149 | DriverFactoryRegistry $driver_factory_registry |
||
150 | ) { |
||
151 | 156 | $this->_eventDispatcher = $event_dispatcher; |
|
152 | 156 | $this->_driverFactoryRegistry = $driver_factory_registry; |
|
153 | |||
154 | 156 | if ( $this->defaults['driver'] ) { |
|
155 | 156 | $this->setDriver($this->defaults['driver']); |
|
156 | 156 | } |
|
157 | 156 | } |
|
158 | |||
159 | /** |
||
160 | * Returns type of browser configuration. |
||
161 | * |
||
162 | * @return string |
||
163 | */ |
||
164 | 8 | public function getType() |
|
168 | |||
169 | /** |
||
170 | * Returns an array of event names this subscriber wants to listen to. |
||
171 | * |
||
172 | * @return array The event names to listen to |
||
173 | */ |
||
174 | 27 | public static function getSubscribedEvents() |
|
175 | { |
||
176 | return array( |
||
177 | 27 | BrowserTestCase::TEST_SETUP_EVENT => array('onTestSetup', 100), |
|
178 | 27 | BrowserTestCase::TEST_ENDED_EVENT => array('onTestEnded', 100), |
|
179 | 27 | ); |
|
180 | } |
||
181 | |||
182 | /** |
||
183 | * Attaches listeners. |
||
184 | * |
||
185 | * @param BrowserTestCase $test_case Test case. |
||
186 | * |
||
187 | * @return self |
||
188 | */ |
||
189 | 40 | public function attachToTestCase(BrowserTestCase $test_case) |
|
190 | { |
||
191 | 40 | $this->_testCase = $test_case; |
|
192 | 40 | $this->_eventDispatcher->addSubscriber($this); |
|
193 | |||
194 | 40 | return $this; |
|
195 | } |
||
196 | |||
197 | /** |
||
198 | * Detaches listeners. |
||
199 | * |
||
200 | * @return void |
||
201 | */ |
||
202 | 10 | protected function detachFromTestCase() |
|
203 | { |
||
204 | 10 | $this->_testCase = null; |
|
205 | 10 | $this->_eventDispatcher->removeSubscriber($this); |
|
206 | 10 | } |
|
207 | |||
208 | /** |
||
209 | * Returns associated test case. |
||
210 | * |
||
211 | * @return BrowserTestCase |
||
212 | * @throws \RuntimeException When test case not attached. |
||
213 | */ |
||
214 | 44 | public function getTestCase() |
|
215 | { |
||
216 | 44 | if ( $this->_testCase === null ) { |
|
217 | 3 | throw new \RuntimeException('Test Case not attached, use "attachToTestCase" method'); |
|
218 | } |
||
219 | |||
220 | 41 | return $this->_testCase; |
|
221 | } |
||
222 | |||
223 | /** |
||
224 | * Sets aliases. |
||
225 | * |
||
226 | * @param array $aliases Browser configuration aliases. |
||
227 | * |
||
228 | * @return self |
||
229 | */ |
||
230 | 148 | public function setAliases(array $aliases = array()) |
|
231 | { |
||
232 | 148 | $this->aliases = $aliases; |
|
233 | |||
234 | 148 | return $this; |
|
235 | } |
||
236 | |||
237 | /** |
||
238 | * Initializes a browser with given configuration. |
||
239 | * |
||
240 | * @param array $parameters Browser configuration parameters. |
||
241 | * |
||
242 | * @return self |
||
243 | * @throws \InvalidArgumentException When unknown parameter is discovered. |
||
244 | */ |
||
245 | 31 | public function setup(array $parameters) |
|
246 | { |
||
247 | 31 | $parameters = $this->prepareParameters($parameters); |
|
248 | |||
249 | // Make sure, that 'driver' parameter is handled first. |
||
250 | 28 | if ( isset($parameters['driver']) ) { |
|
251 | 28 | $this->setDriver($parameters['driver']); |
|
252 | 28 | unset($parameters['driver']); |
|
253 | 28 | } |
|
254 | |||
255 | 28 | foreach ( $parameters as $name => $value ) { |
|
256 | 25 | $method = 'set' . ucfirst($name); |
|
257 | |||
258 | 25 | if ( !method_exists($this, $method) ) { |
|
259 | 3 | throw new \InvalidArgumentException('Unable to set unknown parameter "' . $name . '"'); |
|
260 | } |
||
261 | |||
262 | 22 | $this->$method($value); |
|
263 | 25 | } |
|
264 | |||
265 | 25 | return $this; |
|
266 | } |
||
267 | |||
268 | /** |
||
269 | * Merges together default, given parameter and resolves aliases along the way. |
||
270 | * |
||
271 | * @param array $parameters Browser configuration parameters. |
||
272 | * |
||
273 | * @return array |
||
274 | */ |
||
275 | 31 | protected function prepareParameters(array $parameters) |
|
279 | |||
280 | /** |
||
281 | * Sets Mink driver to browser configuration. |
||
282 | * |
||
283 | * @param string $driver_name Mink driver name. |
||
284 | * |
||
285 | * @return self |
||
286 | * @throws \InvalidArgumentException When Mink driver name is not a string. |
||
287 | */ |
||
288 | 157 | public function setDriver($driver_name) |
|
289 | { |
||
290 | 157 | if ( !is_string($driver_name) ) { |
|
291 | 3 | throw new \InvalidArgumentException('The Mink driver name must be a string'); |
|
292 | } |
||
293 | |||
294 | 157 | $this->_driverFactory = $this->_driverFactoryRegistry->get($driver_name); |
|
295 | 157 | $this->_mergedDefaults = self::arrayMergeRecursive($this->defaults, $this->_driverFactory->getDriverDefaults()); |
|
296 | |||
297 | 157 | return $this->setParameter('driver', $driver_name); |
|
298 | } |
||
299 | |||
300 | /** |
||
301 | * Sets Mink driver options to browser configuration. |
||
302 | * |
||
303 | * @param array $driver_options Mink driver options. |
||
304 | * |
||
305 | * @return self |
||
306 | */ |
||
307 | 6 | public function setDriverOptions(array $driver_options) |
|
311 | |||
312 | /** |
||
313 | * Sets hostname to browser configuration. |
||
314 | * |
||
315 | * To be called from TestCase::setUp(). |
||
316 | * |
||
317 | * @param string $host Hostname. |
||
318 | * |
||
319 | * @return self |
||
320 | * @throws \InvalidArgumentException When host is not a string. |
||
321 | */ |
||
322 | 25 | public function setHost($host) |
|
323 | { |
||
324 | 25 | if ( !is_string($host) ) { |
|
325 | 3 | throw new \InvalidArgumentException('Host must be a string'); |
|
326 | } |
||
327 | |||
328 | 22 | return $this->setParameter('host', $host); |
|
329 | } |
||
330 | |||
331 | /** |
||
332 | * Sets port to browser configuration. |
||
333 | * |
||
334 | * To be called from TestCase::setUp(). |
||
335 | * |
||
336 | * @param integer $port Port. |
||
337 | * |
||
338 | * @return self |
||
339 | * @throws \InvalidArgumentException When port isn't a number. |
||
340 | */ |
||
341 | 24 | public function setPort($port) |
|
342 | { |
||
343 | 24 | if ( !is_int($port) ) { |
|
344 | 3 | throw new \InvalidArgumentException('Port must be an integer'); |
|
345 | } |
||
346 | |||
347 | 21 | return $this->setParameter('port', $port); |
|
348 | } |
||
349 | |||
350 | /** |
||
351 | * Sets browser name to browser configuration. |
||
352 | * |
||
353 | * To be called from TestCase::setUp(). |
||
354 | * |
||
355 | * @param string $browser_name Browser name. |
||
356 | * |
||
357 | * @return self |
||
358 | * @throws \InvalidArgumentException When browser name isn't a string. |
||
359 | */ |
||
360 | 26 | public function setBrowserName($browser_name) |
|
361 | { |
||
362 | 26 | if ( !is_string($browser_name) ) { |
|
363 | 3 | throw new \InvalidArgumentException('Browser must be a string'); |
|
364 | } |
||
365 | |||
366 | 23 | return $this->setParameter('browserName', $browser_name); |
|
367 | } |
||
368 | |||
369 | /** |
||
370 | * Sets default browser url to browser configuration. |
||
371 | * |
||
372 | * To be called from TestCase::setUp(). |
||
373 | * |
||
374 | * @param string $base_url Default browser url. |
||
375 | * |
||
376 | * @return self |
||
377 | * @throws \InvalidArgumentException When browser url isn't a string. |
||
378 | */ |
||
379 | 13 | public function setBaseUrl($base_url) |
|
380 | { |
||
381 | 13 | if ( !is_string($base_url) ) { |
|
382 | 3 | throw new \InvalidArgumentException('Base url must be a string'); |
|
383 | } |
||
384 | |||
385 | 10 | return $this->setParameter('baseUrl', $base_url); |
|
386 | } |
||
387 | |||
388 | /** |
||
389 | * Sets desired capabilities to browser configuration. |
||
390 | * |
||
391 | * To be called from TestCase::setUp(). |
||
392 | * |
||
393 | * @param array $capabilities Desired capabilities. |
||
394 | * |
||
395 | * @return self |
||
396 | * @link http://code.google.com/p/selenium/wiki/JsonWireProtocol |
||
397 | */ |
||
398 | 32 | public function setDesiredCapabilities(array $capabilities) |
|
402 | |||
403 | /** |
||
404 | * Sets server timeout. |
||
405 | * To be called from TestCase::setUp(). |
||
406 | * |
||
407 | * @param integer $timeout Server timeout in seconds. |
||
408 | * |
||
409 | * @return self |
||
410 | * @throws \InvalidArgumentException When timeout isn't integer. |
||
411 | */ |
||
412 | 9 | public function setTimeout($timeout) |
|
413 | { |
||
414 | 9 | if ( !is_int($timeout) ) { |
|
415 | 3 | throw new \InvalidArgumentException('Timeout must be an integer'); |
|
416 | } |
||
417 | |||
418 | 6 | return $this->setParameter('timeout', $timeout); |
|
419 | } |
||
420 | |||
421 | /** |
||
422 | * Sets session strategy name. |
||
423 | * |
||
424 | * @param string $session_strategy Session strategy name. |
||
425 | * |
||
426 | * @return self |
||
427 | */ |
||
428 | 40 | public function setSessionStrategy($session_strategy) |
|
432 | |||
433 | /** |
||
434 | * Tells if browser configuration requires a session, that is shared across tests in a test case. |
||
435 | * |
||
436 | * @return boolean |
||
437 | */ |
||
438 | 42 | public function isShared() |
|
442 | |||
443 | /** |
||
444 | * Sets parameter. |
||
445 | * |
||
446 | * @param string $name Parameter name. |
||
447 | * @param mixed $value Parameter value. |
||
448 | * |
||
449 | * @return self |
||
450 | * @throws \LogicException When driver wasn't set upfront. |
||
451 | */ |
||
452 | 163 | protected function setParameter($name, $value) |
|
453 | { |
||
454 | 163 | if ( !isset($this->_driverFactory) ) { |
|
455 | throw new \LogicException('Please set "driver" parameter first.'); |
||
456 | } |
||
462 | |||
463 | /** |
||
464 | * Returns parameter value. |
||
465 | * |
||
466 | * @param string $name Name. |
||
467 | * |
||
468 | * @return mixed |
||
469 | * @throws \InvalidArgumentException When unknown parameter was requested. |
||
470 | */ |
||
471 | 103 | protected function getParameter($name) |
|
481 | |||
482 | /** |
||
483 | * Creates driver based on browser configuration. |
||
484 | * |
||
485 | * @return DriverInterface |
||
486 | */ |
||
487 | 7 | public function createDriver() |
|
493 | |||
494 | /** |
||
495 | * Returns session strategy hash based on given test case and current browser configuration. |
||
496 | * |
||
497 | * @return string |
||
498 | */ |
||
499 | 12 | public function getSessionStrategyHash() |
|
509 | |||
510 | /** |
||
511 | * Returns test run status based on session strategy requested by browser. |
||
512 | * |
||
513 | * @param BrowserTestCase $test_case Browser test case. |
||
514 | * @param \PHPUnit_Framework_TestResult $test_result Test result. |
||
515 | * |
||
516 | * @return boolean |
||
517 | * @see IsolatedSessionStrategy |
||
518 | * @see SharedSessionStrategy |
||
519 | */ |
||
520 | 10 | public function getTestStatus(BrowserTestCase $test_case, \PHPUnit_Framework_TestResult $test_result) |
|
530 | |||
531 | /** |
||
532 | * Returns checksum from current configuration. |
||
533 | * |
||
534 | * @return integer |
||
535 | */ |
||
536 | 44 | public function getChecksum() |
|
542 | |||
543 | /** |
||
544 | * Similar to array_merge_recursive but keyed-valued are always overwritten. |
||
545 | * |
||
546 | * Priority goes to the 2nd array. |
||
547 | * |
||
548 | * @param mixed $array1 First array. |
||
549 | * @param mixed $array2 Second array. |
||
550 | * |
||
551 | * @return array |
||
552 | */ |
||
553 | 163 | protected static function arrayMergeRecursive($array1, $array2) |
|
570 | |||
571 | /** |
||
572 | * Allows to retrieve a parameter by name. |
||
573 | * |
||
574 | * @param string $method Method name. |
||
575 | * @param array $args Arguments. |
||
576 | * |
||
577 | * @return mixed |
||
578 | * @throws \BadMethodCallException When non-parameter getter method is invoked. |
||
579 | */ |
||
580 | 106 | public function __call($method, array $args) |
|
590 | |||
591 | /** |
||
592 | * Hook, called from "BrowserTestCase::setUp" method. |
||
593 | * |
||
594 | * @param TestEvent $event Test event. |
||
595 | * |
||
596 | * @return void |
||
597 | */ |
||
598 | 24 | public function onTestSetup(TestEvent $event) |
|
606 | |||
607 | /** |
||
608 | * Hook, called from "BrowserTestCase::run" method. |
||
609 | * |
||
610 | * @param TestEndedEvent $event Test ended event. |
||
611 | * |
||
612 | * @return void |
||
613 | */ |
||
614 | 10 | public function onTestEnded(TestEndedEvent $event) |
|
622 | |||
623 | } |
||
624 |