Mock Objects and Testing Controllers
Up to now I assumed mock objects were just dummy objects you used in your unit tests when you didn’t want to use a real object, e.g. because the real object goes off and does something weird to a remote server. But apparently Mocks Aren’t Stubs, they are objects that allow you to do a different style of testing. Most units tests involve verifying the correct state of an object after an interaction, with mocks you can make sure the right methods are being called in the first place (see Martin Fowler’s article for more).
Most of the time I wouldn’t want to test the interaction, I just want to test an object, so mocks aren’t needed, but one area of my programming that always falls short of testing is controllers (in the MVC sense). They just require too many bits and pieces to get running that you eventually end up running your entire program for one test. Then I came across a couple of posts from Robert Chatley, who was in the same research group as me at university. He talks about using Spring’s mocks, as well as using JMock. That’s pretty much what I wanted to do and it all looked very easy.
Imagine we have a controller that does a Lucene search, we want to make sure it’s actually doing the search, here’s an example test case using JMock:
public SearchControllerTest extends MockObjectTestCase {
private Mock searcher;
private Mock logger;
private SearchController controller;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
public void setUp() {
super.setUp();
this.mockSearcher = mock(IndexSearcher.class);
this.mockLogger = mock(QueryLogger.class);
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.request.setMethod(“GET”);
this.controller = new SearchController();
this.controller.setSearcher((IndexSearcher) searcher.proxy());
this.controller.setQueryLogger(logger.proxy());
}
public void testSearch() {
// Set our search term
this.request.setParameter(“q”, “apples”);
// Set our expectations
Mock mockHits = mock(Hits.class);
mockHits.stubs().method(“length”).will(returnValue(10));
this.mockSearcher.expects(once()).method(“search”).will(returnValue(hits));
// Do the request
ModelAndView modelAndView = this.controller.handleRequest(this.request,
this.response);
// Verify our hits are stored in the model
Map model = modelAndView.getModel();
assertEquals(model.get(“hits”, mockHits.proxy());
}
public void testSearchWithNoResults() {
// Set our search term
this.request.setParameter(“q”, “apples”);
// Set our expectations
Mock mockHits = mock(Hits.class);
mockHits.stubs().method(“length”).will(returnValue(0)); // We have an empty hits object
// We’re not checking this time, just providing a stub to return our
// hits object
this.mockSearcher.stubs().method(“search”).will(returnValue(hits));
// Because we have no results, we expect this query to be logged.
this.mockLogger.expects(once()).method(“noHits”);
// Do the request
ModelAndView modelAndView = this.controller.handleRequest(this.request,
this.response);
}
}
In the above example we have two test cases, the first checks a search is executed correctly, and the second checks a special case where we log the query if it returns no results. We setup several mock objects, and for each one you have to state what calls you expect to be made on it, and what (if any) return value should be given.
There are two ways to get a mock to respond to a method, either with expects() or stubs(). Use expects if you want to test the method call. You’ll have to specify none(), once() or atLeastOnce(). Use stubs if you just want it to return a value, e.g. the length of our Hits object.
One nice thing about JMock is that the tests are sentence like, it’s very easy to read and understand what a line is doing, e.g. the searcher expects the method ’search’ to be called once. One (major) problem with JMock is the completely barren Javadocs. There is no documentation for the MockObjectTestCase class. This makes it pretty hard to write a test unless you’re copying someone else’s example.
This above example is very simple, but it’s possible to test error conditions, page flow and various other niggly bits of controllers, which means we can be confident of their behaviour and no longer have to ‘run and pray’.

June 18th, 2006 at 4:17 am
Nice, thanks for the example, Miles.