21

I am trying to test a method of my controller in a Spring Boot application. This is a post endpoint, which gets an id in a request and it passes on this id to a service:

@Slf4j
@Controller
public class AdministrationController {

    private final AdministrationService administrationService;

    @Autowired
    public AdministrationController(AdministrationService administrationService) {
        this.administrationService = administrationService;
    }

    @PostMapping("/administration")
    public @ResponseBody ResponseEntity<String> deleteByMessageId(String id) {
        return new ResponseEntity<>(administrationService.deleteMessageById(id), HttpStatus.OK);
    }
}

The test for this method of the controller:

RunWith(SpringRunner.class)
@WebMvcTest(AdministrationController.class)
public class AdministrationControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private AdministrationService service;

    @Test
    public void 
    deleteByMessageId_whenCalled_thenServiceMethodIsCalledWithRequestParameters() throws Exception {

        Object randomObj = new Object() {
            public final String id = "1234";
        };

        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(randomObj);


        MvcResult result = mvc.perform(
            post("/administration")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(json))
            .andExpect(status().isOk())
            .andReturn();

        verify(service, times(1)).deleteMessageById("1234");
}

}

When I run this test, the post request is executed, but with an empty body:

MockHttpServletRequest:
  HTTP Method = POST
  Request URI = /administration
   Parameters = {}
      Headers = {Content-Type=[application/json]}
         Body = <no character encoding set>
Session Attrs = {}

It seems, even though I set the content in my test, it does not appear in the request I am sending. And, indeed, the test fails:

Argument(s) are different! Wanted: "1234"
Actual invocation has different arguments: null

What am I missing here? How can I set the request body with MockMvc?

handris
  • 1,999
  • 8
  • 27
  • 41

4 Answers4

53

Try to use .characterEncoding("utf-8")) :

MvcResult result = mvc.perform(post("/administration")
    .contentType(MediaType.APPLICATION_JSON)
    .content(json)
    .characterEncoding("utf-8"))
    .andExpect(status().isOk())
    .andReturn();
  • The order has to be characterEncoding, content, contentType – Lee Meador May 17 '19 at 18:39
  • 1
    Are you sure? I think it doesnt matter – stelios.anastasakis May 21 '19 at 05:02
  • I seem to remember that that order mattered to my IDE. I haven't checked. But this order does work. – Lee Meador May 29 '19 at 18:14
  • 4
    I think I know where Lee Meador is coming from. The ordering does not matter. The problem is that there is one extra parenthesis somewhere in your code or you are missing one. .andExpect() comes after you close mvc.perform(). .andExpect() does not get chained to post(). I happened to have this problem just now. – DP Park Jun 27 '19 at 11:34
3

Another option (that is inspired by @stelios-anastasakis answer) is to provide character encoding within the Content-Type header:

MvcResult result = mvc.perform(post("/administration")
    .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
    .content(json)
    .andExpect(status().isOk())
    .andReturn();

Note that we use MediaType.APPLICATION_JSON_UTF8_VALUE instead of MediaType.APPLICATION_JSON here.

Slava Semushin
  • 14,904
  • 7
  • 53
  • 69
0
        mockMvc.perform(post( "/request")
                .content(objectMapper.writeValueAsString(requestBody))
                .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
                .andDo(print())
                .andExpect(status().isOk());
-1

You are surely missing the annotation for your method's parameter.

public @ResponseBody ResponseEntity<String> deleteByMessageId(String id)

Should be something like:

public @ResponseBody ResponseEntity<String> deleteByMessageId(@RequestParam("id") String id)

If you want to use it as a request parameter.

Take a look at the Spring MVC documentation here. Of course writing the test depends on how you expect the id here.

Arnold Galovics
  • 3,246
  • 3
  • 22
  • 33