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:
•Java SE: System.getProperty(String name) •Java ME: RecordStore.openRecordStore(String recordStoreName, boolean createIfNecessary)
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.
public class RecordStore { public static RecordStore openRecordStore(String recordStoreName, boolean createIfNecessary) throws RecordStoreException, RecordStoreFullException, RecordStoreNotFoundException { // implementation goes here } // ... }
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.
public class MessageDao { private static final String RECORD_STORE_NAME = “MessageDao”; public void addMessage(Message message) throws RecordStoreException { RecordStore recordStore = null; try { // This static method invocation is normally hard to mock recordStore = RecordStore.openRecordStore(RECORD_STORE_NAME, true); byte[] bytes = encodeMessage(message); if (message.getId() == -1) { message.getId() = recordStore.addRecord(bytes, 0, bytes.length); } else { recordStore.setRecord(message.getId(), bytes, 0, bytes.length); } } finally { if (recordStore != null) { recordStore.closeRecordStore(); } } } }
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.
public class MessageDaoTest extends TestCase { private MessageDao messageDao; // Object under test private RecordStore recordStoreMock; // Mock object // Called by JUnit to initialize the test public void setUp() { // Create mock object for RecordStore as usual recordStoreMock = createMock(RecordStore.class); // tell mocking framework that static methods of class RecordStore // should be mocked staticMock(RecordStore.class); // Create object under test messageDao = new MessageDao(); } // Called by JUnit to clean up after each test run public void tearDown() { messageDao = null; } public void testAddMessage() throws Exception { // Set up expected behaviour // (This usually doesn’t work) expectStatic(RecordStore.openRecordStore(“MessageDao”, true)) .andReturn(recordStoreMock); byte[] expectEncMess = “u0004fromu0004text”.getBytes(); expect(recordStoreMock.addRecord(aryEq(expectEncMess), eq(0), eq(10))) .andReturn(42); recordStoreMock.closeRecordStore(); // Put the mocks in test state replayStatic(RecordStore.class); replay(recordStoreMock); // Executing the code we want to test Message message = new Message(“from”, “text”); messageDao.addMessage(message); assertEquals(“Wrong message ID”, 42, message.getId()); // Verify the behaviour verifyStatic(RecordStore.class); verify(recordStoreMock); } }
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.
public class StaticMock { static Mapmocks = new HashTable (); public static void mock(Class type) { MockInvocationHandler h = new MockInvocationHandler((MocksControl) EasyMock.createControl()); mocks.put(type, h); } public static void replay(Class type) { mocks.get(type).getControl().replay(); } public static void verify(Class type) { mocks.remove(type).getControl().verify(); } }
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:
public Object mockMethodAdvice(ProceedingJoinPoint jp) throws Throwable { // find the InvocationHandler associated with the class Class type = jp.getSignature().getDeclaringType(); InvocationHandler h = mocks.get(type); // if there is no InvocationHandler let the method call proceed // as normal if (h == null) { return jp.proceed(); } // otherwise redirect the method call to the proxy MethodSignature methodSignature = ((MethodSignature)jp.getSignature()); return h.invoke(type, methodSignature.getMethod(), jp.getArgs()); }
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.
@Aspect public class StaticMockAll extends StaticMock { @Pointcut(“call(static * *(..))”) public void anyStaticOperation() {} @Around(“anyStaticOperation()”) public Object aroundStaticMethods(ProceedingJoinPoint jp) throws Throwable { return mockMethodAdvice(jp); } }
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.