Qafoo GmbH - passion for software quality

Help you and your team benefit from new perspectives on cutting-edge quality engineering techniques and tools through the Qafoo team weblog.

By Benjamin Eberlei, first published at Tue, 11 Jul 2017 09:59:39 +0200

Download our free e-book "Crafting Quality Software" with a selection of the finest blog posts as PDF or EPub.

Crafting Quality Software

You can also buy a printed version of the book on Amazon or on epubli.

Refactoring Singleton Usage to get Testable Code

So your code base is littered with singletons and using them? Don't worry, you can start refactoring them out of your code base class by class and introduce increased testability at every step. This strategy is very simple to implement and the propability of breaking your code is very low, especially when you are becoming more experienced with this technique.

Take the following example code of a SearchService that acceses a singleton to perform its work:

class SearchService { public function searchAction($queryString, $type) { /** @var $solarium \Solarium_Client */ $solarium = Solarium::getInstance(); $select = $solarium->createSelect(); // More and complex filtering logic to test $result = $solarium->query($select); return $result; } }

To make this code testable without the singleton, we can use the lazy initialization pattern. The first step is to extract the method for the line that is fetching the singleton:

public function searchAction($queryString, $type) { /** @var $solarium \Solarium_Client */ $solarium = $this->getSolarium(); // ... } protected function getSolarium() { return Solarium::getInstance(); }

You now have two options for testability. The most obvious is to create a test class that extends the original SearchService and overwrites the protected getSolarium to return a mock. But it is not very flexible and additional classes necessary for testing are not a good practice to follow.

Instead introduce a new instance variable and fetch the singleton only if this is null, making use of the so called lazy initialization pattern:

private $solarium; private function getSolarium() { if ($this->solarium === null) { $this->solarium = Solarium::getInstance(); } return $this->solarium; } public function setSolarium(\Solarium_Client $solarium) { $this->solarium = $solarium; }

Since you would want to use constructor injection for all mandatory dependencies you could also introduce an optional constructor argument, like:

private $solarium; public function __construct(\Solarium_Client $solarium = null) { $this->solarium = $solarium ?: Solarium::getInstance(); }

Now this code is already testable using mocks:

class SearchServiceTest extends PHPUnit_Framework_TestCase { public function testSearchFilter() { $solariumMock = \Phake::getMock(SolariumClient::class); $service = new SearchService($solariumMock); \Phake::when($solarium)->createSelect()->thenReturn(new \Solarium_Query_Select($solarium)); $service->search('Foobar', 'some_type'); \Phake::verify($solarium)->query(\Phake::capture($select)); // Perform assertions on $select } }

If you perform this refactoring often you can entirely remove singletons from parts of your code base and move towards a more testable dependency injection.

Download our free e-book "Crafting Quality Software" with a selection of the finest blog posts as PDF or EPub.

Crafting Quality Software

You can also buy a printed version of the book on Amazon or on epubli.

Get Technical Insights With Our Newsletter

Stay up to date with regular new technological insights by subscribing to our newsletter. We will send you articles to improve your developments skills.

Comments

  • Damian Dziaduch on Thu, 14 Dec 2017 14:05:54 +0100

    Good stuff! Thank you Benjamin :)

  • spider solitaire on Thu, 13 Sep 2018 12:23:39 +0200

    Here friends will find you a very useful place for your bored moment or you are able to play free spider solitaire games on the gadget, it can make the most valuable moment for you without any interruption