Static Mock using AspectJ

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 mocks = new HashMap();

	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.

This Post Has 2 Comments

  1. 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.

Leave a Reply

Close Menu