Sunday, March 23, 2008

Mockito - The New Mock Framework on the Block

xUnit Patterns by Gerard Meszaros brought the idea of Test Spies to the front of my mind last Fall. I'll summarize my previous post by saying there are stubs and mocks, which most people are aware of, but there are also Test Spies. They lie between stubs and mocks in terms of complexity, and are characterized by having many features of mocks without the record/playback baggage that comes with the most popular mock frameworks. In my humble opinion, my previous post on the topic is worth reading.

When you're writing your own hand-rolled mock objects, test spies are an easy, logical extension beyond a simple stub. If your system under test is calling a void method, foo( ), and you want to make sure that method is called, then you write a wasFooCalled( ) method on the mock and verify it in your test. Simple and effective.

Let's consider the near-canonical example of an Order object (a product ID and a quantity) and a Warehouse object which can fill orders.

public interface Warehouse {
boolean remove(String id, Integer quantity);
}

public class Order {

private final String id;
private final Integer quantity;

public Order(String ID, Integer quantity) {
this.id = ID;
this.quantity = quantity;
}

public boolean fill(Warehouse warehouse) {
return warehouse.remove(id, quantity);
}
}

The Order.fill( ) method collaborates with the Warehouse object, and our test should verify that interaction. A hand rolled mock of Warehouse might look like this:

class WarehouseMock implements Warehouse {
boolean wasCalled = false;

public boolean remove(String id, Integer quantity) {
wasCalled = true;
return true;
}

boolean removeWasCalled() {
return wasCalled;
}
}

And the Order unit test might look like this:

WarehouseMock warehouse = new WarehouseMock();

Order order = new Order("TALISKER", 50);
order.fill(warehouse);

Assert.assertTrue(
"Warehouse must have remove() called",
warehouse.removeWasCalled("TALISKER", 50));

Enter Mockito, a new test spy framework that takes the drudgery out of hand rolling your own "wasXcalled" methods. Here is the same test with Mockito:

Warehouse warehouse = Mockito.mock(Warehouse.class);
Order order = new Order("TALISKER", 50);
order.fill(warehouse);

Mockito.verify(warehouse).remove("TALISKER", 50);

Those familiar with record/playback frameworks like EasyMock will notice the lack of a set expectations step or a replay step. This is still a very simple test. You mock out warehouse, you execute some methods on the class under test, and then you verify that the mock was used correctly. Compare this to the EasyMock version:

Warehouse warehouse = EasyMock.createMock(Warehouse.class);
EasyMock.expect(warehouse.remove("TALISKER", 50))
.andReturn(true)
.once();
EasyMock.replay(warehouse);

Order order = new Order("TALISKER", 50);
order.fill(warehouse);

EasyMock.verify(warehouse);

Mockito really cleans up the unit test by not requiring expectations. Personally, I much prefer the Mockito API to the EasyMock API for that reason. Now, there are still times when you need to set expectations. Mockito works great if you're making sure a void method gets called (testing indirect output), but often you need to supply indirect input to your class via the mock, and in those cases you really do want to set some expectations. Since Mockito is a fork of EasyMock, you still have the option to do this if you choose. Feel free to set expectations when you want, you just aren't forced to in all cases.

Mockito has a couple other nice features too, like being able to mock classes, a good attempt at producing better error messages, and some annotation magic for mock instantiation. As the author, Szczepan Faber says, "Hats down before EasyMock folks for their ideas on beautiful and refactorable mocking syntax." It's a great framework that has served me well for years, but I for one am switching to Mockito for the time being. Great work, everyone involved!

5 comments:

Robert Fischer said...

Aren't you comparing apples to oranges? It looks like your EasyMock 1) has a redundant "once" call, and 2) specifies a return value. Your Mockito doesn't specify a return value to that call.

Hamlet D'Arcy said...

In this test I'm not interested in the return value of order.fill() or warehouse.remove(), yet a mock object will make you specify this information. A mock object should be used for supplying indirect input to the system under test, while a test spy should be used to observe indirect output.

Stubs are on one end of the continuum in that they don't supply meaningful values to the system under test. Test Spies don't supply value either, but they can be used to observe which methods were called within the system under test. Mocks do it all by supplying indirect input via expectations and verifying indirect output via verify(mock) calls.

Each serves a purpose. Creating a mock when only a stub is needed would be overkill. Creating a mock when only a spy is needed is also overkill (but before Mockito I wasn't using a library that had this feature).

I think there is room for all 3 types of "test doubles": stubs, spies, and mocks.

Peter Pascale said...

I really like that it keeps the simple case simple. The simple case being where you don't need to set expectations.

jenslaufer said...

I have compared EasyMock with Mockito in some very simple example. Maybe it's a good starting point before going into deeper.
http://www.laufer-online.com/tech/entry/mock_testing_frameworks_for_white

木須炒餅Jerry said...
This comment has been removed by a blog administrator.