Mocking objects that are “new”-ed up.
I was reading this article by Misko Hevery and it reminded me of times when I use to get frustrated with unit tests because the code under test was “too difficult” to test as a unit. Either objects weren’t simple to create and mock or they were “new-ed” up in the class under test. Well, my whole perspective changed once I was introduced to Jmockit.
Jmockit is, according to the site, “a collection of tools and APIs for use in developer testing…” Personally, I define Jmockit as “a magic jar that makes things easier.” You can use whichever definition you prefer.
So lets jump right into it! First things first; download the jar from here and add it the almighty classpath! Alright, were all set, lets get going.
Here is our contrived but educational scenario:
public class ClassWeNeedTested {
public String methodWeNeedTested(String param) {
//do stuff
Helper myHelper = new Helper();
String data = myHelper.retrieveSomeData(param);
//do stuff with data
return data;
}
}
And the code for the Helper class:
public class Helper {
public String retrieveSomeData(String param) {
//connect to a database
//do some craziness
return "data";
}
}
So as you can see, its a simple example. We have a method on a class that is going to use some Helper class to retrieve data based on the input parameter. We don’t want the Helper class hitting any database or really doing anything at all when we are testing so we need to be able to mock that class out. Enter Jmockit:
import static org.junit.Assert.assertEquals;
import mockit.Mockit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class ClassWeNeedTestedTest {
ClassWeNeedTested classUnderTest = null;
@Before
public void setUp() {
classUnderTest = new ClassWeNeedTested();
}
@After
public void tearDown() {
Mockit.tearDownMocks();
}
@Test
public void testMethodWeNeedTested() {
Mockit.redefineMethods(Helper.class, MockHelper.class);
String result = classUnderTest.methodWeNeedTested("param");
assertEquals("Must be 'mock data'", "mock data", result);
}
public static class MockHelper {
public String retrieveSomeData(String param) {
return "mock data";
}
}
}
Lets examine what is happening here, though it isn’t much. The first line of testMethodWeNeedTested() is where all the magic happens. Mockit.redefineMethods() takes two classes as parameters and intercepts calls made to the first class with matching method calls on the second. We need to create a class to take the calls instead, which is exactly what I do at the bottom of the test class. MockHelper is just a simple little static class that returns a string of “mock data” in the retrieveSomeData() method. If everything goes to plan, when ClassUnderTest makes its call to Helper, it will instead be calling MockHelper without realizing it! We may not want these mocks to last the entire time the JVM is running so we make sure to call tearDownMocks() in our tearDown() method. If there were another test method after testMethodWeNeedTested, it wouldn’t be aware of our MockHelper and would need the same redefineMethods() line. Alternatively, you could move this code up into the setUp() but it all depends on your individual testing needs. Alright, enough jabber, lets run it and see what happens!
Chances are, it didn’t work for you. You are probably receiving an error similar to, “Jmockit has not been initialized. Check that your Java…” In order for Jmockit to do its magic, you have to tell the JVM about it. In order to do this, you need to add the following to your VM arguments in your IDE when launching your Junit tests.
-javaagent:"C:\path\to\jmockit\jar.jar"

Jmockit Config
After doing this, Jmockit will be able to work its magic and you should see that beautiful green bar staring back at you!
Couple notes before we conclude:
1. You have to be using Java 1.5 or higher for Jmockit to work properly. The magic done by Jmockit uses some JVM instrumentation features that didn’t exist until 1.5.
2. Your mock class must have the exact same method signature of the method being mocked. I know this seems obvious but better to let you know than have you wondering why exceptions are being thrown.
3. This isn’t even close to everything that Jmockit is capable of. For a complete picture of Jmockit and all its parts, check out their site.
4. For those running maven, you can add the Jmockit javaagent to your surefire “argLine” as follows:
...//inside the build stanza
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
...//version/etc...
<configuration>
<argLine>
-javaagent:"path/to/jmockit/jar.jar"
</argLine>
...//rest of surefire configuration
Leave a Reply