REST Web Services testing with Spring MVC


A previous post introduced the basic features of the Spring MVC test framework. This post covers the testing of a REST web controller: by checking the response status, the content type, and the returned JSON document , we'll verify if the controller behaves as expected.

The controller to test

The controller to test is rather simple, it just returns a Java object that ends up being serialized in the response. The point here is to test Spring MVC features like argument mapping and serialization.

@Controller
public class ContactController {
 
    @RequestMapping("/contact/{id}")
    @ResponseBody
    public Contact contact(@PathVariable("id") Integer id) {
        // mimics a call to a business service
        return new Contact(id,"Test Firstname","Test Lastname");
    }
 
}

The test of the controller

Setting up the test

We need to set up the web version (@WebAppConfiguration) of the Spring TestContext Framework (@RunWith(SpringJUnit4ClassRunner.class) and @ContextConfiguration). We embed the configuration in the test, thanks to a @Configuration inner class. Here is the setup part of the test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class ContactControllerTest {
 
    @Autowired
    private WebApplicationContext ctx;
 
    private MockMvc mockMvc;
 
    @Before
    public void setUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
    }
 
    (...)
 
    @Configuration
    @EnableWebMvc
    public static class TestConfiguration {
 
        @Bean
        public ContactController contactController() {
            return new ContactController();
        }
 
    }
 
}

Note we use @EnableWebMvc on the configuration class: this will activate (among others) the REST support. We need this to register the Jackson (JSON) converter in charge of serialization.

The infrastructure is ready, let's send a (mock) request to our controller.

Sending a request, debugging the response

The whole point of the test is to ensure the controller responds correctly to one of our requests. Our controller is quite simple, so we could send a request and directly check the result with the appropriate expectation API. For more complex cases, like fancy custom JSON serialization, we would typically send the request and use some debugging features to display the response. This would make the writing of the expectations simpler (especially if the returned document is somewhat complex). Let's do that!

Our first version of the test method sends the request and use andDo(print()) to display the request and response information on the console:

@Test
public void contact() throws Exception {
    Integer id = 1;
    mockMvc.perform(get("/contact/{id}",id).accept(MediaType.APPLICATION_JSON))
            .andDo(print());
}

The execution of the test should output the following on the console:

MockHttpServletRequest:
         HTTP Method = GET
         Request URI = /contact/1
          Parameters = {}
             Headers = {Accept=[application/json]}

             Handler:
                Type = com.zenika.ContactController
              Method = public com.zenika.Contact com.zenika.ContactController.contact(java.lang.Integer)

               Async:
   Was async started = false
        Async result = null

  Resolved Exception:
                Type = null

        ModelAndView:
           View name = null
                View = null
               Model = null

            FlashMap:

MockHttpServletResponse:
              Status = 200
       Error message = null
             Headers = {Content-Type=[application/json;charset=UTF-8]}
        Content type = application/json;charset=UTF-8
                Body = {"id":1,"firstname":"Test Firstname","lastname":"Test Lastname"}
       Forwarded URL = null
      Redirected URL = null
             Cookies = []

In real-world tests, you would typically check the response status, the headers, and the body:

Headers = {Content-Type=[application/json;charset=UTF-8]}
Content type = application/json;charset=UTF-8
Body = {"id":1,"firstname":"Test Firstname","lastname":"Test Lastname"}

Let's see how to do that.

Checking the response

We check the response by calling the andExpect method on the ResultActions object returned by the perform method. The andExpect needs a ResultMatcher object and we would typically use ResultMatchers provided by the MockMvcResultMatchers class.

To check the response code, we use the status() method. We expect 200 (OK):

.andExpect(status().isOk())

To check the content type:

.andExpect(content().contentType("application/json;charset=UTF-8"))

Testing the JSON document can be a pain, especially if you don't have support for JSON parsing. The good news is Spring MVC test framework supports JsonPath out-of-the-box. Retrieving the value of the id attribute is like the following:

.andExpect(jsonPath("id").value(id));

Let's put it all together, here is the full test method:

@Test
public void contact() throws Exception {
  Integer id = 1;
  mockMvc.perform(get("/contact/{id}",id).accept(MediaType.APPLICATION_JSON))
         .andExpect(status().isOk())
         .andExpect(content().contentType("application/json;charset=UTF-8"))
         .andExpect(jsonPath("id").value(id));
}

Pretty simple, isn't it? Launch the test in your favorite IDE to see the green bar!

And here's the full version of the test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class ContactControllerTest {
 
    @Autowired
    private WebApplicationContext ctx;
 
    private MockMvc mockMvc;
 
    @Before
    public void setUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
    }
 
    @Test
    public void contact() throws Exception {
        Integer id = 1;
        mockMvc.perform(get("/contact/{id}",id).accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().contentType("application/json;charset=UTF-8"))
                .andExpect(jsonPath("id").value(id));
    }
 
    @Configuration
    @EnableWebMvc
    public static class TestConfiguration {
 
        @Bean
        public ContactController contactController() {
            return new ContactController();
        }
 
    }
 
}

Conclusion

This post covered how to test a Spring MVC REST controller with the Spring MVC out-of-container test framework. We managed to check the controller in terms of input parameters, response status, response headers, and response content. By testing a read-only web service, we only scratched the surface of the framework: one can test the other HTTP operations (POST, PUT, DELETE), requests with some content in the body, more headers, etc. Now, get back to work and test your REST web service layer!

Source code


Commentaires

1. Le mardi 20 août 2013, 18:51 par abrasadera

Hi
Great post and everything clearly explained.
I was wondering how to compare string from response body (not json) with that andExpect()
I know that mockMvc has andReturn() so whole expression can return response - but I wonder that is possible to check simple string against that in body within mockMvc expression?

Fil des commentaires de ce billet

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.