Unit testing annotation based Spring MVC controllers


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 {

    private MockHttpServletRequest request;
    private MockHttpServletResponse response;
    private MyController controller;
    private 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.

Information and Links

Join the fray by commenting, tracking what others have to say, or linking to it from your blog.


Other Posts

Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

I had the same experience going back from Restlet 1.2 to 1.1, where 1.2 uses annotations and thus you can easily test the “plain” @Get, @Post etc methods. Restlet 1.1 uses inheritance, which makes it much harder to test. That’s really an advantage of annotations.

Hi!
I saw, that you helped somebody to create Hibernate test with AbstractTransactionalHibernateTest JUnit class

Where can I get this JUnit import? Is there a documentation about this JUnit?

Gruß Christopher

I think using EasyMock and EasyMockUnitils is easier for testing @MVC controller .

I have no idea why pure mapping URL testing is neccessary, though.

I just tried your code and I’m getting
java.lang.IllegalArgumentException: pkg.MyController is not an interface
at java.lang.reflect.Proxy.getProxyClass(Proxy.java:362)
197)

error as MyController isn’t an interfacce. But yours isn’t either. Do you have any idea why that might be? I’m using Junit 4 and the latest EasyMock (2.5.2)

Jörg

Don’t worry. Learned about the class extension. A classic RTFM case …