Search code examples
javaspring-bootspring-mvcspring-mvc-test

MockHttpServletResponse has empty in test, despite Controller working correctly


I'm testing a @RestController POST endpoint using MockMvc. In this test, the MockHttpServletResponse content is always empty & the content type is not set. I checked the controller method; it's getting called and returns the right response. The status code is correctly set to 201 though, in the test.

Here's the test :

@Test
public void testBookCreation() throws Exception {
    var response = mockMvc.perform(
        MockMvcRequestBuilders
            .post("/books/create")
            .contentType(MediaType.APPLICATION_JSON)
            .content(
                jsonb.toJson(new Book("KR","K & R",5,8))
            )
    ).andExpect(
        MockMvcResultMatchers.status().isCreated()
    ).andExpect(
        MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)
    ).andExpect(
        MockMvcResultMatchers.jsonPath("$.author").exists()
    ).andDo((result)->{
        System.out.println(result.getResolvedException());
        System.out.println(result.getResponse());
    }).andReturn();
    }

and here's the controller method:

@PostMapping("/create")
@ResponseStatus(code=HttpStatus.CREATED,reason="Book created")
public Book makeBook(@RequestBody Book b ) {
    var res = bookRepository.save(b);
    System.out.println(res);
    return res;
}

I think the problem is with the test and not the controller itself.


Solution

  • The documentation for ResponseStatus states the following:

    Warning: when using this annotation on an exception class, or when setting the reason attribute of this annotation, the HttpServletResponse.sendError method will be used.

    Meaning that you will receive a response of following shape, no matter what would be the response sent payload in your controller:

    {
      "timestamp":"",
      "status":201,
      "error":"Created",
      "message":"",
      "path":"/books/create"
    }
    

    You should then remove the reason field for the ReponseStatus annotation:

    @PostMapping("/create")
    @ResponseStatus(code=HttpStatus.CREATED)
    public Book makeBook(@RequestBody Book b ) {
        var res = bookRepository.save(b);
        System.out.println(res);
        return res;
    }