Mocking Hard Dependencies in Legacy Code
I’m working on an application that’s been around for a while. It uses a lot of static methods, and often, it instantiates objects from inside methods. This makes it a pain to test, and introducing tests is part of my job. Sometimes I’m able to refactor the code so that dependencies may be injected, but many times I can’t confidently refactor the code without breaking something somewhere else—because I don’t have enough tests. It’s a chicken-and-egg problem.
Recently, I found a way to test some of this untestable code, using Mockery with PHPUnit.
Let’s say we have a model class representing a User that looks something like this:
class User
{
protected $address;
protected $addressId;
public function setAddressId($addressId)
{
$this->addressId = $addressId;
return $this;
}
public function getAddressId()
{
return $this->addressId;
}
public function getAddress()
{
if ($this->getAddressId() === null) {
return null;
}
if ($this->address === null) {
$this->address = new Address($this->getAddressId());
}
return $this->address;
}
}
Testing most of this is a piece of cake. Here’s how our first stab at test cases might look:
class UserTest extends PHPUnit_Framework_TestCase
{
public function testGetSetAddressId()
{
$user = new User();
$this->assertNull($user->getAddressId());
$this->assertSame($user, $user->setAddressId(123));
$this->assertEquals(123, $user->getAddressId());
}
public function testGetAddressIsNull()
{
$user = new User();
$this->assertNull($user->getAddress());
}
}
Now, we just need to test the getAddress()
method to ensure it returns an Address object, but how are we going to do that? The $address
property is protected, so we can’t easily set it. An easy change is to set it public. Then, after instantiating a User, we can set an Address to it and assert that getAddress()
returns it.
But what if we can’t do that? What if our interface needs to stay the same?
Well, we can use reflection to change the accessibility of protected properties, so let’s try that.
public function testGetAddressIsAlreadySet()
{
$user = new User();
$user->setAddressId(123);
$reflection = new ReflectionClass('User');
$property = $reflection->getProperty('address');
$property->setAccessible(true);
$property->setValue($user, new Address(123));
$this->assertInstanceOf('Address', $user->getAddress());
}
Great! Now, our coverage is better, and we’ve asserted the getAddress()
method returns an Address object. The problem is we didn’t test whether the getAddress()
method sets the $address
property, and we have an ugly, untested line in our code-coverage report that’s keeping us from hitting that glorious 100% coverage mark.

Address is a hard dependency. We can’t change it. Since the object is instantiated within the method, there’s no way for us to inject a test double, so we can’t test it without fear of causing side effects. In this simple example, the side effects may be harmless—maybe it simply makes a SELECT
query against a database to retrieve data—but this turns into an integration test, and we don’t want to test our integration with the database. We only want to test our code.
One way to handle this is to admit defeat and quietly sweep away the untested lines under the rug by wrapping them with @codeCoverageIgnoreStart
and @codeCoverageIgnoreEnd
annotations. This is what I used to do when encountering code that appeared untestable.
// @codeCoverageIgnoreStart
if ($this->address === null) {
$this->address = new Address($this->getAddressId());
}
// @codeCoverageIgnoreEnd
In researching approaches to testing code, I stumbled accross the “Mocking Hard Dependencies” section of the Mockery documentation. Mockery provides the ability to inject a test double class into the scope of the running test, and when your code under test invokes that class—with the new
keyword or a static method—it uses your mock.
This works best when using an autoloader in your tests. If any code is encountered that uses a class that hasn’t yet been loaded, it invokes the autoloader to load the class. Mockery allows you to overload this behavior by injecting a class with the same name, so that when the class is encountered in your code, it is already loaded, and the autoloader doesn’t try to load your real class. If you’re using Composer’s autoloader with your tests, you’re all set.
This is not without some problems, though. If previous tests have already autoloaded the class, then Mockery won’t be able to inject a class with the same name. This will result in an error:
Mockery\Exception\RuntimeException: Could not load mock Address, class already exists
Thankfully, PHPUnit has functionality that allows us to isolate tests, so we can get around this problem. Just add the annotations @runInSeparateProcess
and @preserveGlobalState
disabled to the test (more information).
Our test using Mockery to mock the hard dependency looks like this:
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testGetAddressHardDependency()
{
$addressMock = Mockery::mock('overload:Address');
$user = new User();
$user->setAddressId(123);
$this->assertInstanceOf('Address', $user->getAddress());
}
Using the “overload:” prefix, Mockery creates a new class named Address and injects it into the scope of the test. Then, when the test calls $user->getAddress()
and invokes new Address()
, the Address object created uses Mockery’s mocked Address class and not our real Address class. What’s best? None of the lines in our coverage report are skipped, and every line is green!
While I’ve shown an effective way to mock hard dependencies, this can lead to brittle tests. The best approach is to use dependency injection. For legacy code, this often involves a lot of refactoring. If you’re unable to invest the time or it’s not worth the cost to refactor, then this approach to mock hard dependencies might come in handy.