Came across a situation today where I need to mock out a static method on one of Websphere’s classes to simulate a certain test case. Normally this kind of bad boy is pretty tricky since you’ve either got to wrap the vendor’s class with your own injectable wrapper, or provide your own impl for their interface. The call in question was:
WSSubject.getCallerSubject();
with WSSubject being a static and final class for good measure. No mocking of the interface possible, no subclassing tricks available; I thought it might be time to check out what is achievable with the various bytecode manipulation libraries to see if I could just change the single method I was interested in. Had a look at ASM and BCEL, and found myself in way too deep in bytecode semantics. In the end I came across Javassist which was at just the right level. Worked my way through the tutorial and I was good to go.
As long as you get to the class before the classloader does, you can go crazy: add methods, substitute in your own methods, add before and after interceptors, change field visibility, the whole nine yards. So I put in a little block in my test case’s static initialiser to hook myself into the process:
static { try { ClassPool cp = ClassPool.getDefault(); CtClass cc = cp.get("com.ibm.websphere.security.auth.WSSubject"); CtMethod subjMethod = cc.getDeclaredMethod("getCallerSubject"); subjMethod.setBody(cp.get(MyTest.class.getName()).getDeclaredMethod("getCallerSubject"), null); cc.toClass(); } catch (Exception e) { e.printStackTrace(); } }
Grab a handle to the WSSubject
class, grab a handle to the getCallerSubject()
method, then use that setBody()
call to insert your own method body in the mix. In my case, I call the getCallerSubject()
on my test class to manufacture my own subject to return. Two words: Sen Sational.
Would probably have been a lot easier wrap the getSubject() call in my own injectable class, but it’s handy to know that all sorts of bytecode kungfu is possible if your motivated.
Big props to Javassist dudes.