Refactoring Singleton Usage to get Testable Code

First published at Tuesday, 11 July 2017

This blog post has first been published in the Qafoo blog and is duplicated here since I wrote it or participated in writing it.

Warning: This blog post is more then 7 years old – read and use with care.

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.

Subscribe to updates

There are multiple ways to stay updated with new posts on my blog: