Spring Controller testing with MockMvc post method
Asked Answered
P

4

22

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?

Pullover answered 21/4, 2018 at 13:22 Comment(0)
A
54

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();
Acceptation answered 6/9, 2018 at 21:29 Comment(4)
The order has to be characterEncoding, content, contentTypeMiddleoftheroad
Are you sure? I think it doesnt matterAcceptation
I seem to remember that that order mattered to my IDE. I haven't checked. But this order does work.Middleoftheroad
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.Hubblebubble
R
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.

Runck answered 16/11, 2018 at 11:0 Comment(1)
APPLICATION_JSON_UTF8_VALUE is deprecatedJeannajeanne
T
0
        mockMvc.perform(post( "/request")
                .content(objectMapper.writeValueAsString(requestBody))
                .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
                .andDo(print())
                .andExpect(status().isOk());
Tipcat answered 14/4, 2022 at 13:15 Comment(0)
G
-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.

Gethsemane answered 21/4, 2018 at 17:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.