Common questions from people trying to introduce unit tests in a legacy code base are
“How do I unit test code which calls a static method?”
“How can I mock a static method?”
The situation often is like the picture below. We have a class that we want to test. We try to isolate that class as much as possible, but fail to properly isolate it because it makes call to static methods which has some side effects in the system.
Adapting the test leads to poor tests
While there are mocking tools which may fake these static methods, I feel that is the wrong way to go. This technique makes the test harder coupled to the implementation, because a large part of the logic from the static method now gets copied into the test setup.
Another option is to initialize the data needed for that static method to run, removing the need to isolate the class under test from the static dependency. This often includes setting system properties, or in a HTTP-based system, putting things on the session or request. Doing this ranges from inconvenient to virtually impossible, depending on how complex setup is needed.
All in all, I don’t believe these situations are best solved by being clever in the test.
Try turning the static method into a non-static one
The simple truth is that using static methods is basically not compatible with unit testing – static methods produce code with low “testability”. Therefore, the “obvious” and often best solution is to turn the static method into a non-static one. Let the class under test create its own instance of it, or provide an instance as a dependency. How to do this depends much on what the method does. If it has no side effects this is often rather simple, and this may be the best way forward. In some cases, you can move the method to one of its parameters. However, many times this is very hard, especially when the method has important side effects.
Wrap the static method in a wrapper object
Often, these static methods are old, complex, used in many places, and because of their static nature affecting the whole system. Perhaps such a method is responsible for returning a singleton object, keeping track of whether we are in some certain mode or not, or similar.
In these cases, a way forward is to create an intermediate object, a wrapper. This object takes the place of the class with the static method in the class under test, and in its first implementation it simply delegates to the static method in question. This wrapper object can then be provided as a dependency to the class under test. In the unit test, we now have the power to create a faked version of the wrapper which we provide to the class under test. This allows us to stub, spy or mock as much as we want. A setup like this for the above example could look as follows.
When we have created the wrapper, we can deprecate the old method, encouraging everyone to use the wrapper instead. After a while, when all invocations of the static method has been changed to go through the wrapper, we can re-implement the wrapper to provide an own, real implementation. Then, finally, we can remove the static method.
Perhaps the most obvious drawback is that if we have a large number of static methods, we tend to get a large number of wrapper classes. This should hopefully be a temporary state until all invocations have been updated, though in many systems I can see how this would become a rather permanent state.
Actually deprecating the static method in favor of the wrapper could at least help to alleviate this somewhat, to make other developers aware of the wrapper’s existence. Another way to reduce the problem is to let a single class act as wrapper for multiple static methods. This of course works best if the methods are logically related.