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
Each Month in 2016
Online
Software Expert Days - 2016
Join us online to talk directly with our Software experts during online Expert Days. Find information here about past, current, and upcoming Expert Da...
Read more
Sep 30
Seattle, WA
OpenStack Days Seattle
OpenStack Days Seattle, September 30, is the largest gathering of OpenStack users and prospective users in the Pacific Northwest region.
Read more
View all