Testing Methods That Make Static Calls

January 13th, 2016

I had trouble testing a particularly painful codebase. It had static calls and implicit dependencies all over the place, to name just a few problems.

One of the things that it often did was to call static methods that would increment counters in the database and cache stuff. Example:

Record::incrementViews()
 It was making things difficult.

To avoid messing with the original codebase too much, I came up with this quick and dirty way to ignore those dependencies. In my unit test, I'd write 

$this->mockStaticDependency('Record');
  and then make all static calls on it to return null:

public function mockStaticDependency($className)
{
    eval('class '.$className.' { public static function __callStatic($name, $arguments) { return null; } }');
}

It's like overloading the class with a dummy catch-all method. Yes, I know, eval is EVIL.

I think that it would be quite simple to expand this with an array of method names and their output, in case those pesky dependencies return something the Method Under Test needs. But that's for another day.

I just wanted to throw it out there, since I saw many questions on this topic without satisfying answers. I know that dealing legacy code is hard enough without testability problems.

Update 2016-01-16

As per the suggestion in the comments, I tried Patchwork. It was brilliant! Not only does it avoid issues when repeatedly mocking the same class, it allows you to easily mock specific methods and provide any implementation, like returning test data. Here is how to do the same as above with Patchwork:

Patchwork\replace('Record::incrementViews', function() {
    return null;
});

Regarding adding an implementation, say we have a method 

Record::findById($id)
. We can use Patchwork to return the data we need.

Patchwork\replace('Record::findById', function($id) {
    return array('id'=>$id, 'name'=>'Anna');
});

In short, Patchwork is really the way to go to test untestable code.

Previous: Better Link Previews on Social Media Next: Fixing Doctrine Segfaults