Friday, May 6, 2011

Mocking frameworks vs Unit Testing style

I've never paid much attention on what mocking framework I use. I've used EasyMock and JMock, I observed no big difference between the two really. Recently I started to change some existing JMock unit tests to use Mockito instead. It seemed to have a more elegant interface, and it turned out this had other implications on my tests than mere syntactic sugar. It actually gave some new insight on the subject.

A little toy code to illustrate this:
public interface GreetingFormula {
 String formula();
}

public class Greeting {
 private GreetingFormula gf;

 public Greeting(GreetingFormula gf) {
  this.gf = gf;
 }

 public String greet(String name) {
  return gf.formula() + " " + name;
 }
}
In my Unit Test for Greeting I want a Mock object for the GreetingFormula interface. I want to verify that formula() has been actually called, and also want to define some return value for it's method. A JMock implementation would look like this:
public class GreetingTest {
 private final static String formulaText = "Hello";
 private final static String name = "Mr. Doe"
 private final static String expectedGreeting = "Hello Mr. Doe";

 private Mockery mockingContext;
 private GreetingFormula greetingFormula;
 private Greeting greeting;
 
 @Test
 public void greetPutsNameIntoFormula() {
  mockingContext.checking(new Expectations() {
   {
    one(greetingFormula).formula();
    will(returnValue(formulaText));
   }
  });
  Assert.assertEquals(expectedGreeting, greeting.greet());     
 }

 @Before
 public void setUp() {
  mockingContext = new JUnit4Mockery();
  greetingFormula = mockingContext.mock(GreetingFormula.class);
  greeting = new Greeting(greetingFormula);    
 }
}
The solution may look ok at first glance but there is a subtle issue here. In expectations two very different concepts are mixed together: how the mock object should behave (return the right value), and what should be verified on the mock object afterwards (it has been called exactly once). Lets take a look at how the same thing is done using Mockito.
public class GreetingTest {
 private final static String formulaText = "Hello";
 private final static String name = "Mr. Doe"
 private final static String expectedGreeting = "Hello Mr. Doe";

 private GreetingFormula greetingFormula;
 private Greeting greeting;
 
 @Test
 public void greetPutsNameToFormula() {
  when(greetingFormula.formula()).thenReturn(formulaText);
  assertEquals(expectedGreeting, greeting.greet());
  verify(greetingFormula).formula();     
 }

 @Before
 public void setUp() {
  greetingFormula = mock(GreetingFormula.class);
  greeting = new Greeting(greetingFormula);    
 }
}
Mockito enforces the clear separation of the concepts of mock object behavior and verification by its design, there is simply no other valid way of doing it. This might not look like a big deal - on a simple example like this - but in fact it is. Unit test code with a lot of mockery does gain a lot of clarity simply by expressing these steps in the right order:

  • mock behaviour

  • perform test

  • mock verification

3 comments:

  1. I enjoy the 'test-spy' mocking style because it follows the standard test flow (set-up, verification, teardown). Besides Mockito, there are a few other testing frameworks that I've come across that support this style

    * Bourne (https://github.com/thoughtbot/bourne) - Ruby
    * RR (https://github.com/btakita/rr) - Ruby
    * Jasmine (https://github.com/pivotal/jasmine/wiki/Spies) - JavaScript

    Check 'em out!

    ReplyDelete
  2. A slight nit: your usage of times(1) isn't necessary (perhaps you did it to illustrate some point that's gone over my head), since verify(mockObject) implicitly uses times(1). Nothing major, but it doesn't hurt to reduce the verbosity of code by just a tinge. :)

    ReplyDelete
  3. verify(greetingFormula, times(1)).formula(); is not necessary. You have already verified it with this line:
    when(greetingFormula.formula()).thenReturn(formulaText);

    ReplyDelete