After seeing MockME developed by some of my colleagues I started thinking about how this could be made in a generic way. For example how would we mock the following class?
public class StupidSingleton { public static String doStatic(int i) { throw new UnsupportedOperationException("method not implemented yet..."); } public static void sayHello() { System.out.println("Hello"); } public static native void nativeMethod(); }
We would like to write something like this:
public void testMocking() { mock(StupidSingleton.class); expect(StupidSingleton.doStatic(17)).andReturn("17"); StupidSingleton.sayHello(); StupidSingleton.nativeMethod(); replay(StupidSingleton.class); assertEquals("17", StupidSingleton.doStatic(17)); StupidSingleton.sayHello(); StupidSingleton.nativeMethod(); verify(StupidSingleton.class); }
After playing around with AspectJ I discovered that this is actually possible! Two things are needed:
* An aspect that allows us to catch calls to static methods!
* An implementation of the mock, replay, verify for classes
This turns out to be really easy! The following aspect catches ALL calls to ALL static methods. Might be a bit aggressive, but it works! If you want you can implement your own aspect with a more specialized pointcut.
@Aspect public class StaticMockAll extends StaticMockAspect { @Pointcut("call(static * *(..))") public void anyStaticOperation() {} @Around("anyStaticOperation()") public Object aroundStaticMethods(ProceedingJoinPoint jp) throws Throwable { return super.aroundStaticMethods(jp); } }
The mock, replay, verify methods turns out to be trivial:
public class StaticMock { static Map<Class, MockInvocationHandler> mocks = new HashMap<Class, MockInvocationHandler>(); public static synchronized void mock(Class type) { MockInvocationHandler h = new MockInvocationHandler((MocksControl) EasyMock.createControl()); mocks.put(type, h); } public static synchronized void replay(Class type) { mocks.get(type).getControl().replay(); } public static synchronized void verify(Class type) { mocks.remove(type).getControl().verify(); } }
Finally, the base class for the aspect that does the actual work is also quite simple:
public class StaticMockAspect { public Object aroundStaticMethods(ProceedingJoinPoint jp) throws Throwable { Class type = jp.getSignature().getDeclaringType(); InvocationHandler h = getInvocationHandler(type); if (h == null) { return jp.proceed(); } MethodSignature methodSignature = ((MethodSignature)jp.getSignature()); return h.invoke(type, methodSignature.getMethod(), jp.getArgs()); } private MockInvocationHandler getInvocationHandler(Class type) { synchronized (StaticMock.class) { return StaticMock.mocks.get(type); } } }
h3. Notes
Things to note and think about:
* Static methods and native methods can be mocked! By using the "call" pointcut we change all code that tries to call the native methods and not the native method itself.
* AspectJ is required to make this work. I have been using Eclipse AJDT
* It should be possible to replace the Aspect with "plain" byte code manipulation and "just" reload the class.
Jan Kronquist
Consultant at Jayway

2 comments ↓
I’ve done something similar when I have had the source. Use AspectJ for compile time weaving, then use Spring for DI in the aspect itself. There are two reasons I took this root. The class is not Spring Managed and Spring itself cannot intercept a static method.
The problem I have now is I need to do something similar to a class that I do not have the source for. AspectJ has support for LTW (Load Time Weaving). What I was thinking is I could use @AspectJ for LTW and Spring for DI.
The first scenario works beautifully, but I cannot get my second scenario to work.
We have actually taken this idea a step further using our own custom classloader and created an open source tool that does static mock (among many other things). Take a look at http://blog.jayway.com/tag/powermock/ or http://powermock.org.
Leave a Comment