2012-03-12

Unit testing code that uses static methods

Static calls are death to testability. There are ways of mocking out static calls made inside a class, but if you have some code that makes static calls outside a class, it's much more difficult. The best way is to refactor the static class to an instance class so you can mock things out.


But if you're working in a legacy code base with large and ugly static classes, refactoring them may be overly difficult or hard to justify from a business perspective. But you still want to test the new code that you're writing that uses those static classes. A coworker showed me one way to do it. It's still hacky and not as good as fixing the static class properly.

Basically instead of refactoring the crufty static code, you do the refactoring in the class you're writing. Here's an example class that uses the static Calculator class from above:


You can't mock out the Calculator class in this case. But you can make changes to your Foo class that will let you control the output from the static class. You can add protected methods to your class that call the static methods, and then mock those methods for testing and test the mock of the class you're testing.


Obviously this example is very simplistic, but in a more complicated class you could have the static class throw exceptions or different output or whatever.

Now you have two problems

This technique is far from perfect. If you were to change the behavior of the protected methods of your class, the unit tests would still pass, but the code would no longer work the same in production.

2 comments:

  1. The example seems to highlight one of the possible flaws with the solution, which is when it calls $this->add( $bar, $bar ). Shouldn't the second $bar be $baz? This would probably be caught by additional test cases, of course.

    If only we all had unlimited time and money to rebuild horror code, right? This at least gets part of the way there. Thanks for sharing the idea :)

    ReplyDelete
  2. Good catch. I've updated the code to suck a little bit less.

    ReplyDelete