Last week, in order to write a test in a way that assumed less about the internal class, I got to the point where the best approach would be to mock an internal method, responsible for loading some metadata. I could test this from the behaviour of the class with my passed inputs, but for this I would have to know what logic resides inside it. This was, however, no good here as this was not the logic to be tested.

Our scenario

We use jMockit as our mocking framework in the project and almost exclusively with the Expectations API. This API provides an invoke method, which can, in turn be used to mock internal methods. Logically, I followed this route, but hit a (major) snag right after it.

My method has a simple but, for this topic, tricky signature

private void loadMetadata(String id, Map<String, String> arguments)
What invoke does is that, through Reflection at runtime, it will determine the type of the parameters and, together with the method name, it will determine what is the best match method in the class. There is, however, one basic flaw to this approach, which some background on Java generics will point out

Java Generics: not like .NET

In .NET, generics are first class citizens and an integral part of .NET 2.0+. They are compiled to bytecode and are understood by the CLI. Java, however, uses generics more as an afterthought, in that they are not understood by the JVM. When compiling a Java application, all generic classes will suffer what is called erasure. What this means is that they will be translated to their bounds, or at its limit, to Object. The compiler does a bunch of other stuff like casting to maintain consistency, but the point is that the bytecode contains nothing more than ordinary classes. A simple run with invoke will show just how naive this approach is

Invoking our way through

Using jMockit's Expectations API, on can declare in the Expectations block

invoke(obj, "loadMetadata", "id", args); times=1;
which means that the loadMetadata method on object obj with "id" and args as arguments. As args is a generic Map, which is an interface, the object actually passed is an HashMap<String, String>. So what happens when you run the test?
java.lang.IllegalArgumentException: No compatible method found: loadMetadata(java.lang.String, java.util.HashMap)
So what happened here? Quite simply, the generics were erased, from HashMap<String,String> into HashMap at runtime. This will not matched our declared signature as stated above. So, we're out of luck. Or are we?

Why jMockit pays

One thing I had to endure from my team colleagues for a while was why did I choose jMockit as a mocking framework when it is harder to use than most. And I always present the same reason: flexibility!

jMockit packs not one but three APIs for testing, Expectations, Verifications and Annotations. Until now I had no reason to use Annotations (noted as to being the most powerful of the three), but some testing actually proved that this is easily done with it. Annotations is, however, more verbose and harder to read for someone not familiar with it.

Annotations is, from the start, a partial mocking API. This means that when you are mocking any class, you specify only what methods you want to mock and the remaining will remain as they are in the original implementation. Also, taking advantage that in jMockit you mock not instances but entire classes, you can easily mock a specific method in all instances of a given class straight in the JVM (how cool is that?) This does not work in the same way as Expectations' invoke. It will replace the original method signature and content with your code. So, how do we do it?

//jMockit annotations API! woohoo!
 new MockUp<MyClass>() {
 public String magic; //for testing, no getter/setter here
 @Mock
 void loadMetadata(String id, Map<String, String> arguments) {
 assert id.equals(magic);
 }
 }.magic = magic;
We start by declaring a new mock for the desired class and writing our methods. It's important to note that I dropped the private from the method declaration. This is necessary. If you do not, jMockit will error out. This does not, however, change the method's visibility at runtime. Also important is to annotate the method as a Mock. As I need an external string from this object, I declare a public variable and fill it directly when declaring the anonymous class (it can be argued that this variable should be private and a public getter/setter should be used, but for test purposes this is enough.)

And that's it! jMockit will replace the loadMetadata method in every object of MyClass right in the JVM. Running the test with a breakpoint on the assert will show our mock is being triggered as expected.

So this is how you can easily mock an internal method with generic arguments. I'm in no way a jMockit expert, so if you find a better approach to do this, please sound of in the comments.

Bye!