Mocking Static Methods

Mock objects is a very useful technique for unit testing as it allows you to focus your testing efforts on the class being tested instead of on its collaborators. Unfortunately, not everything is an object and therefore you cannot always use mock objects. This article will show how mocking can be generalized to include more things than just objects.

Motivation

When unit testing we would like to test only a single unit of code without any exter-
nal dependencies. However, an object seldom lives in isolation and often has one or
more collaborators, which it communicates with using method calls. Mock objects
is a well known technique (1) that that can be used to simulate the collaborators
and thereby create a totally controlled environment. Mock objects together with
test driven development is also very useful for improving the design as you discover
how your class should be used.
So everything is fine? Well, not everything is an object, even in Java. Sometimes
static methods are used and it is no longer possible to use the mock object technique
as there no longer is an object. But that has a simple solution: “Don’t use static
methods” – right? Sure, if you write all code yourself that is possible, but almost all
projects have external dependencies outside of the project’s control. It is even worse
than you might initially think as Java standard APIs often have lots of static methods.
Some examples:

Example

In the last issue of JayView there was an article about a tool called MockME that
makes it possible to mock the static methods that are abundant in the Java ME
APIs (2). This article tries to solve this problem in a more general way.
Let us use the same code example which uses the RecordStore from Java ME and
examine the problem again. For those of you unfamiliar with Java ME, RecordStore
is an API for storing byte array records persistently on the phone.

We now implement a data access object (DAO) that we use to store messages.
It will allow us to store and access messages from a RecordStore without bothering
with the details how the message is stored.

Desired Solution

In this unit test of MessageDao we only test the happy-flow (when everything goes
fine) to show the principle of static mocking. Please compare this to the MockME
solution.

This test sets up the expected method calls on RecordStore, then invokes the
DAO and finally verifies its behavior. But unfortunately it is not possible to mock
static methods as the binding is done at compile time. Or is it?

Aspects to the rescue

It turns out that this is possible. Using Aspect Oriented Programming (AOP) we
can change the static methods to become mockable. Without going too much into
details, AOP allows us to change the behavior of classes and methods after they have
been compiled. Three things are needed:

  • A pointcut that specifies which methods the aspect should change (that is all
    static methods)

  • An advice that that allows mocking to replace the original behavior
  • An implementation of the mock, replay, verify methods that allows mocking
    classes

Let us start with the setup methods. To keep track of which classes we have
mocked we create a Map that contains an EasyMock MockInvocationHandler for
each class that we mock. MockInvocationHandler is a proxy object that performs
the actual mocking and will receive method invocations instead of the real object.
Notice the use of a Hashtable to make the map accesses thread safe.

Then once we intercept a method call to a static method, find out if there is a
MockInvocationHandler associated with that class and if so redirect the call to it.
This method should not be static to avoid infinite recursion. The following advice
does the trick:

Finally we need to tell AspectJ which methods it should apply the above advice
to. The following aspect uses a pointcut that catches ALL calls to ALL static meth-
ods (call(static * *(..))). It might be a bit aggressive, but it works! If you want you can
implement your own aspect with a more specialized pointcut, for example specific
to your own application.

Things to note:

  • Static methods can be mocked!
  • Native methods can also be mocked this way! By using the “call” pointcut we
    change all code that tries to call the native methods and not the native method
    itself. However, note that class loading issues are not avoided. For example, Java
    ME implementations typically require that native libraries are loaded which
    can cause problems when unit testing.

  • AspectJ is required to make this work. I have been using Eclipse AJDT plugin
    when writing and testing this code.

Please download and view the sample code for a simple working example of
using this technique. Go to www.jayway.se/jayview and download the zip file as-
sociated with this issue.

Discussion

There are other are ways to get around this problem. For example you could write
small wrappers around these static methods and then consistently use the wrappers
instead of the underlying implementation. This works, but each project will have
its own wrappers which will probably make it harder to understand than using
standard APIs.
The approach taken by MockME is also an option, especially for handling stand-
ard APIs such as the APIs for Java ME. However, this adds slightly more complexity
to writing the tests, as you need to use the MockME specific methods.
This new solution proposal is still very experimental, but using AOP or some
other technique for bytecode manipulation seems to be a very promising solution
to the problem of mocking static methods. However, to be successful a packaged
solutions needs to:

  • be a drop in jar file
  • work well with any IDE
  • work with build systems such as ant or maven

Conclusion

So now static method are no longer bad design since we can mock them? No, not
really. Static methods are still bad, but we no longer have to sacrifice or complicate
testability when we are forced to use static methods. When writing new code you
should use an object oriented design and use mock objects for your unit tests. The
mocking techniques presented in this article are only a tool for testing things that
weren’t testable before. This allows us to further increase the possible code coverage
of our tests, but our design rules should not change.

References

Sample code available here

1. Endo-Testing: Unit Testing with Mock Objects
2. JayView 13 – Java ME Testing Done Right
3. EasyMock
4. JUnit
5. AspectJ
6. Eclipse AJDT
7. AOP

Originally published in JayView.

Leave a Reply

Close Menu