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
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
ReplyDelete* 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!
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. :)
ReplyDeleteverify(greetingFormula, times(1)).formula(); is not necessary. You have already verified it with this line:
ReplyDeletewhen(greetingFormula.formula()).thenReturn(formulaText);