Friday, 11 April 2014

Spring MockMvc tests

MVC Test Framework arrived with Spring 3.2. It allows to write integration tests (well... almost) for your Spring MVC @Controller(s)

One of server-side cornerstones is MockMvc class. It allows to execute requests on your Controllers very easily, but it needs to be initialized before it is used. Threre are two ways to initialize MockMvc and choice depends on how broad integration with other Spring MVC components your tests need. In my case, it was custom view resolver and few others I removed for sake of simplicity.

First is meant for simple single Controller testing

    @Test
    public void foo() {
        TestedController controller = new TestedController();
        MyCustomViewResolver resolver = new MyCustomViewResolver();
        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).setViewResolvers(resolver).build();

        MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.get("/foo")).andReturn();
        //parse and assert response.getContentAsString() as view was resolved by MyCustomViewResolver
    }
You can employ extensive set of spring-mvc usual suspects, like ConversionService, ViewResolvers, MessageConverters, ... see StandaloneMockMvcBuilder javadoc

All this manual assembling makes me feel little unconfortable. Normaly all those beans are wired together in Spring Dispatcher context.

Another way to initlialize MockMvc is using @WebAppConfiguration and MockMvcBuilders.webAppContextSetup(webAppContext)

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = TestMvcSpringConfig.class)
public class FooTests {

    @Autowired
    private WebApplicationContext webAppContext;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
    }

    @Test
    public void foo() {
        MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.get("/foo")).andReturn();
        MockHttpServletResponse response = result.getResponse();
        //parse and assert response.getContentAsString() as view was resolved by MyCustomViewResolver
    }

    @EnableWebMvc
    @Configuration
    public static class TestMvcSpringConfig extends WebMvcConfigurerAdapter {

        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }

        @Bean
        public TestedController getController() {
            return new TestedController();
        }

        @Bean
        public ViewResolver viewResolver() {
            MyCustomViewResolver resolver = new MyViewCustomResolver();
            return resolver;
        }
    }

For some unknown reason (that disappeared as quickly as it appeared) Spring was failing to create @EnableWebMvc annotated @Configuration, complaining about missing servlet context. I had to get my hands dirty and roll my own Spring web context. So for completeness, here is how it was done using MockServletContext. May be useful sometime.


    @Test
    public void test() throws Exception {
        MockServletContext servletContext = new MockServletContext();

        AnnotationConfigWebApplicationContext springContext = new AnnotationConfigWebApplicationContext();
        springContext.setServletContext(servletContext);
        springContext.register(TestMvcSpringConfig.class);
        springContext.refresh();

        MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(springContext).build();

        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/foo")).andReturn();
    }

Happy Spring MVC testing!

No comments:

Post a Comment