Software Developers
Showing results for 
Search instead for 
Do you mean 

A Comprehensive Example of a Spring MVC Application - Part 4

amirkibbar on ‎07-15-2013 05:36 AM

Testing the Web Layer

As can be understood from the large section about implementing the Web Layer, it is obvious that this layer requires rigorous tests. In this layer we want to test the REST API only - not the service. So we'll mock the service.

 

Here we need 2 types of tests:

  • Controller tests - test the functionality of the controller itself
  • View tests - test the ability to render different views

 

In Spring 3.2 there's a new bean called mock MVC which is based on another open source project called spring test mvc: https://github.com/SpringSource/spring-test-mvc. This latter project was merged into Spring 3.2, but if you're on Spring 3.1 or earlier you can use that external project instead. They are almost identical, the main difference is that the package names changed. This spring test extension provides a fluent API for testing controllers.

 

Testing the controller means that we need to create a web application context (as opposed to an application context) and define all the web beans: content negotiation manager, message converters and our mock service. This can be done as follows:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class PersonControllerTest {
    @Autowired
    private PersonController controller;                  // the controller under test
    @Autowired
    private PersonService service;                        // the mock service
    private MockMvc mockMvc;                              // the mock Spring MVC fluent bean

    @PostConstruct
    public void setup() {
        // create the mock Spring MVC fluent bean
        mockMvc = MockMvcBuilders.standaloneSetup(controller)  // create it only for our controller
                // add the message converters
                .setMessageConverters(new MappingJackson2HttpMessageConverter(), new StringHttpMessageConverter())
                // and build it
                .build();
    }

    @Test
    public void json() throws Exception {
        // setup expectations on the service mock
        when(service.readAllPersons()).thenReturn(Arrays.asList(new Person("Moshe", "Cohen"), new Person("Yakov", "Levi")));

        // perform the request
        mockMvc.perform(get("/persons").accept(MediaType.APPLICATION_JSON))         // get /persons, accept json
                .andExpect(status().isOk())                                         // expect the status to be ok
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))       // expect json result
                .andExpect(jsonPath("$.", filter(where("firstName").is("Moshe"))).exists())  // expect Moshe in the list
                .andExpect(jsonPath("$..[?(@.firstName == 'Moshe')]").exists());    // same same (but different syntax)
    }


    // more tests here

     
    @Configuration
    @ComponentScan(basePackages = "com.hp.example.controllers")
    public static class PersonControllerTestConfiguration {
        @Bean
        public PersonService personService() {
            return mock(PersonService.class);
        }
    }
}

 

As you can see the mock MVC fluent bean is created in the post construct method by passing it the controller created in our web application context. Also the message converters are registered to it. Then the mock MVC is used in the test method in a very human readable way.

 

Notice the jsonPath matcher which uses the json-path library as a json parser and matcher: https://code.google.com/p/json-path/.

 

Finally - here's the test of the different view renderers. This test just verifies that the response is in the expected media type. Unlike the individual controller test above, this test is defined with the application's rest-servlet.xml:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration                // make this a web application context test
public class PersonViewTest {
    @Autowired
    private WebApplicationContext wac;
    @Autowired
    private PersonService service;
    private MockMvc mockMvc;

    @PostConstruct
    private void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    public void csv() throws Exception {
        when(service.readAllPersons()).thenReturn(Arrays.asList(new Person("Moshe", "Cohen"), new Person("Yakov", "Levi")));

        getPersonsForMediaType("text/csv");
    }

    @Test
    public void excel() throws Exception {
        when(service.readAllPersons()).thenReturn(Arrays.asList(new Person("Moshe", "Cohen"), new Person("Yakov", "Levi")));

        getPersonsForMediaType("application/vnd.ms-excel");
    }

    private MvcResult getPersonsForMediaType(String mediaType) throws Exception {
        return mockMvc.perform(get("/persons").accept(MediaType.parseMediaType(mediaType)))
                .andExpect(status().isOk())
                .andExpect(content().contentType(mediaType)).andReturn();
    }

    @Configuration
    @ImportResource("WEB-INF/rest-servlet.xml")      // import the bean definitions from the XML
    public static class PersonViewTestConfiguration {
        @Bean
        public PersonService personService() {
            return mock(PersonService.class);
        }
    }
}

 

0 Kudos
About the Author

amirkibbar

Events
Aug 29 - Sep 1
Boston, MA
HPE Big Data Conference 2016
Attend HPE’s Big Data Conference on August 29 - September 1, 2016 to learn from peers in every industry and hear from Big Data experts and thought lea...
Read more
Sep 13-16
National Harbor, MD
HPE Protect 2016
Protect 2016 is our annual conference on September 13 - 16, 2016, and is the place to meet the world’s top information security talent, discuss new pr...
Read more
View all