Recently I stumbled onto JMockit and have been pretty impressed with the flexibility of the approach it takes.

Many mocking frameworks seem to take an elitist attitude toward testable code, not attempting to solve certain problems in favor of guiding one toward a more testable design. It appears JMockit is a response to this.

There's no getting around the fact that some frameworks, especially legacy or proprietary third party modules, are not coded in such a way that it's easy to write testable code against them. Common pain points include:

  • Pervasive use of statics
  • Lack of dependency injection mechanisms
  • Creating dependencies inline with the "new" keyword

All of these issues will pose problems when trying to double-out dependent code for testing purposes. One approach to solve these problems is to use a dynamic language and metaprogramming constructs to do this kind of doubling. Testing Java code with JRuby or Groovy has become more and more popular for this very reason.

But a lot of these will introduce a level of language abstraction between your test code and the code under test, and you'll have to have developers maintaining a test suite sign on to learn the language being used.

In comes JMockit, which uses the instrumentation features provided with Java5 to perform a lot of the same magic tricks for you.

This also allows you to test things that weren't otherwise possible (or are very difficult) and overall I think the programming model is much more in-tune with standard Java programming idioms. It doesn't discriminate against you if you don't use dependency injection, but will work with you if you do. Here's a quick example that colleagues Andy Pemberton and Patrick Cox worked through with me trying out JMockit.

@RunWith(JMockit.class)
public class ControllerTest {
 
 @Mocked
 HttpServletRequest mockHttpServletRequest;
 @Mocked
 ServiceRemote mockServiceRemote;
 
 @Test
 public void test_execute_expectations() {
 
 new Expectations() {
 { /* define in static block */
 final Model m = new Model();
 m.setId(12345l);
 
 mockHttpServletRequest.getParameter("modelId"); returns("12345");
 mockServiceRemote.getModel(12345l); returns(m);
 mockHttpServletRequest.setAttribute("model", m);
 }
 }.endRecording();
 
 /* simulate setter injection */
 Controller c = new Controller();
 c.setHttpServletRequest(mockHttpServletRequest);
 c.setServiceRemote(mockServiceRemote);
 
 /* call code under test */
 c.execute();
 
 /* strict mode will throw exceptions! */
 }
}

You can get the whole project from my GitHub account.