Mocks aren’t stubs (Just a quick review)

Object Oriented Programming

I know that Martin Fowler has written about this extensively, but I wanted to write a concise version, since this point seems to bear reiterating from time to time.

Mocks aren’t stubs.

Specifically, I recommend against setting an expectation on a method (“mocking” it) when you really want to simulate a specific response from that method for the current testing (“stubbing” it). That said, I must confess than I used mocks in place of stubs early in my test-driven development practice, and I believe that all programmers who try the so-called “mockist” style of design eventually use a mock where a stub would work better. If you’ve done this, or are doing this now, then don’t feel shame. Instead, consider changing your practice.

When one uses a mock in place of a stub, one tends to over-specify the collaboration between a Client and its Suppliers, and that leads to brittle tests. These tests tend to make hyperactive assertions, meaning assertions that fail even when the underlying production code works as expected. Such hyperactive assertions play the role of the boy who cried “wolf!”, distracting us with needless concern. Even when these superfluous mocks don’t over-specify a collaboration, they often duplicate specifications in other tests. Even when they don’t do that, they tend to encourage us to make two assertions (method expectation and checking the ModelAndView) or check irrelevant details multiple times, such as:

public class FreeBeansReportHandlesNMatchingCustomersTest {
private JUnit4Mockery mockery = new JUnit4Mockery();

private CustomerRepository customerRepository = mockery.mock(CustomerRepository.class);
private FreeBeansReportController controller = new FreeBeansReportController(customerRepository);

// SMELL Misnomer. Should call this something like
// expectToAskForCustomersBornWithinDateRangeThenPretendToMatchTheseCustomers()
public void pretendToMatchTheseCustomers(DateRange dateRange, Set matchingCustomers) {
mockery.checking(new Expectations() {{

public void zero() {
pretendToMatchTheseCustomers(new DateRange(january(1, 2010), january(7, 2010)), Collections. emptySet());
ModelAndView modelAndView = controller.handleRequest(new HttpServletRequestBuilder().addParameter(“start_date”, “2010-01-01”).build();
assertEquals(“NoFreeBeansToday”, modelAndView.getViewName());

public void notZero() {
Set matchingCustomers = Collections. singleton(anyOldCustomer());
pretendToMatchTheseCustomers(new DateRange(january(1, 2010), january(7, 2010)), matchingCustomers);
ModelAndView modelAndView = controller.handleRequest(new HttpServletRequestBuilder().addParameter(“start_date”, “2010-01-01”).build());
assertEquals(“FreeBeansReport”, modelAndView.getViewName());
assertSame(matchingCustomers, modelAndView.getModel().get(“customers”));

Writing one such test doesn’t seem to matter much, but when the method in question returns a collection, and you write the “zero, one, many, lots, oops” tests, you might mock that method exactly the same way five times. How many times do you need to check the same thing?

Now please go read, or re-read, Martin’s excellent article.