20

I am following a Spring 2.5 tutorial and trying, at the same time, updating the code/setup to Spring 3.0.

In Spring 2.5 I had the HelloController (for reference):

public class HelloController implements Controller {
    protected final Log logger = LogFactory.getLog(getClass());
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        logger.info("Returning hello view");
        return new ModelAndView("hello.jsp");
    }
}

And a JUnit test for the HelloController (for reference):

public class HelloControllerTests extends TestCase {
    public void testHandleRequestView() throws Exception{
        HelloController controller = new HelloController();
        ModelAndView modelAndView = controller.handleRequest(null, null);
        assertEquals("hello", modelAndView.getViewName());
    }
}

But now I updated the controller to Spring 3.0, and it now uses annotations (I also added a message):

@Controller
public class HelloController {
    protected final Log logger = LogFactory.getLog(getClass());
    @RequestMapping("/hello")
    public ModelAndView handleRequest() {
        logger.info("Returning hello view");
        return new ModelAndView("hello", "message", "THIS IS A MESSAGE");
    }
}

Knowing that I am using JUnit 4.9, can some one explain me how to unit test this last controller?

1

3 Answers 3

25

One advantage of annotation-based Spring MVC is that they can be tested in a straightforward manner, like so:

import org.junit.Test;
import org.junit.Assert;
import org.springframework.web.servlet.ModelAndView;

public class HelloControllerTest {
   @Test
   public void testHelloController() {
       HelloController c= new HelloController();
       ModelAndView mav= c.handleRequest();
       Assert.assertEquals("hello", mav.getViewName());
       ...
   }
}

Is there any problem with this approach?

For more advanced integration testing, there is a reference in Spring documentation to the org.springframework.mock.web.

Sign up to request clarification or add additional context in comments.

6 Comments

+1 Thank you so much Sasha. It works great. I couldn't imagine it was so simple.
This won't work if there are @Autowired components in HelloController
@AramKocharyan: in unit tests I would recommend against using Autowired and supply dependencies explicitly, via constructor or setters. If you really want all the Spring goodness you may want to look into static.springsource.org/spring/docs/3.0.x/…
@SashaO yeah learnt that this week :P I'm just setting the autowired instance with a setter as you say, worked well.
I find this approach too white box. The fact that the view is called "hello" doesn't mean is doing what needs to do. We are unit testing our controllers using HttpClient. Just as if the test were the browser.
|
21

With mvc:annotation-driven you have to have 2 steps: first you resolve the request to handler using HandlerMapping, then you can execute the method using that handler via HandlerAdapter. Something like:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("yourContext.xml")
public class ControllerTest {

    @Autowired
    private RequestMappingHandlerAdapter handlerAdapter;

    @Autowired
    private RequestMappingHandlerMapping handlerMapping;

    @Test
    public void testController() throws Exception {
        MockHttpServletRequest request = new MockHttpServletRequest();
        // request init here

        MockHttpServletResponse response = new MockHttpServletResponse();
        Object handler = handlerMapping.getHandler(request).getHandler();
        ModelAndView modelAndView = handlerAdapter.handle(request, response, handler);

        // modelAndView and/or response asserts here
    }
}

This works with Spring 3.1, but I guess some variant of this must exist for every version. Looking at the Spring 3.0 code, I'd say DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter should do the trick.

2 Comments

@I got null pointer exception in Object handler = handlerMapping.getHandler(request).getHandler(); How would i solve
@jackyesind, as suggested in the answer the request must be initialized. Something like new MockHttpServletResponse("GET", "/your/uri");
1

You can also look into other web testing frameworks that are independent of Spring like HtmlUnit, or Selenium. You won't find any more robust strategy with JUnit alone other than what Sasha has described, except you should definitely assert the model.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.