Oliver Drotbohm Archive About Tags

Unit testing annotation based Spring MVC controllers

May 17th, 2009

Although it took me a while to get used to them, I now pretty much like the annotation based programming model for controllers in Spring MVC. The major advantage over the traditional inheritance based approach is, that the methods, that are bound to requests are simple public methods, that can easily be unit tested.

So while unit testing the behaviour of the method is fine, one actually might want to test the mapping of URLs. Current Spring code requires you to invoke AnnotationHandlerMethodAdapter, that is responsible for detecting the method to be invoked AND invoking it. This is rather suboptimal as there is actual a private class contained in it that does the method lookup only. I already filed a request for enhancement to extract that class and thus offer an easy way to test URL mappings.

But that does not mean, that pure mapping testing is impossible right now. As long as you can live with executing the body of the controller methods you’re fine anyway. If that causes too much preparation overhead or you simply don’t like (as I do) there is a solution for you: mocking.

Suppose you have a controller class like this:

@Controller
public class MyController {

  @RequestMapping("/users")
  public void foo(HttpServletResponse response) {
    // Controller code goes here...
  }
}

Now you can set up a mock of this controller using EasyMock classextensions or another mocking framework of your choice and thus only check the URL mappings of your controller:

public class RequestMappingTest {

  MockHttpServletRequest request;
  MockHttpServletResponse response;
  MyController controller;
  AnnotationMethodHandlerAdapter adapter;

  @Before
  public void setUp() {

    controller = EasyMock.createNiceMock(
      MyController.class);

    adapter = new AnnotationMethodHandlerAdapter();
    request = new MockHttpServletRequest();
    response = new MockHttpServletResponse();
  }

  @Test
  public void testname() throws Exception {

    request.setRequestURI("/users");

    controller.foo(response);
    EasyMock.expectLastCall().once();
    EasyMock.replay(controller);

    adapter.handle(request, response, 
      controller);

    EasyMock.verify(controller);
  }
}

You probably want to factor out basic behaviour into a common superclass to easily write mapping tests for all of your controllers.

blog comments powered by Disqus