Testing Methods That Make Static Calls

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:

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:

Regarding adding an implementation, say we have a method  Record::findById($id). We can use Patchwork to return the data we need.

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

4 thoughts on “Testing Methods That Make Static Calls

  1. Gundars

    Not only it’s evil, it’s also mostly disabled, but this still is better than most answers in the subject

  2. Spudley

    For testing code like this, I’ve found the Patchwork library to be invaluable.

    http://antecedent.github.io/patchwork/

    Patchwork basically provides monkey-patching for PHP. It makes it much easier to write good tests for a lot of the crusty code I have to work with.

    Just do Patchwork\replace(‘Classname::method’, function() { /*do nothing*/}); at the start of your test, and Patchwork\undoAll() in your teardown. Job done.

    • Awesome! That’s exactly what I’ve been looking for. I’ll definitely try it. I’ll update the post after that.

      • I thought I’d point out that you may also do the same with Mockery: https://github.com/padraic/mockery

        When testing in this way, you’re injecting a new class with the same name. If using an autoloader, and the real version of the class gets loaded first in a different test, you’ll encounter errors (also true if your eval’d version gets loaded first and a later test expects the real class). So, you’ll need to run your tests in isolation. You can do this with PHPUnit annotations “@runInSeparateProcess” and “@preserveGlobalState disabled”. This method of testing is impossible to use if using “require/include” to include files, rather than an autoloader.

        I gave a recent talk on this, where I showed some examples: https://speakerdeck.com/ramsey/mocking-with-mockery-ski-php-2016?slide=47

        And I wrote an article showing how to test hard dependencies (use of the “new” keyword inside a unit) using mock instances: https://benramsey.com/articles/mocking-hard-dependencies/

        Obviously, these kinds of tests aren’t ideal and there are ways to refactor around this, but it’s great to know we have options for testing legacy code without having to do full rewrites.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">