springspring-mvcexceptionspring-mvc-test

Empty Exception Body in Spring MVC Test


I am having trouble while trying to make MockMvc to include the exception message in the response body. I have a controller as follows:

@RequestMapping("/user/new")
public AbstractResponse create(@Valid NewUserParameters params, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) throw BadRequestException.of(bindingResult);
    // ...
}

where BadRequestException looks sth like this:

@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "bad request")
public class BadRequestException extends IllegalArgumentException {

    public BadRequestException(String cause) { super(cause); }

    public static BadRequestException of(BindingResult bindingResult) { /* ... */ }

}

And I run the following test against /user/new controller:

@Test
public void testUserNew() throws Exception {
    getMockMvc().perform(post("/user/new")
            .param("username", username)
            .param("password", password))
            .andDo(print())
            .andExpect(status().isOk());
}

which prints the following output:

  Resolved Exception:
                Type = controller.exception.BadRequestException

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

            FlashMap:

MockHttpServletResponse:
              Status = 400
       Error message = bad request
             Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
        Content type = null
                Body = 
       Forwarded URL = null
      Redirected URL = null
             Cookies = []

Does anybody have an idea on why is Body missing in the print() output?

Edit: I am not using any custom exception handlers and the code works as expected when I run the server. That is, running the application and making the same request to the server returns back

{"timestamp":1423076185822,
 "status":400,
 "error":"Bad Request",
 "exception":"controller.exception.BadRequestException",
 "message":"binding failed for field(s): password, username, username",
 "path":"/user/new"}

as expected. Hence, there is a problem with the MockMvc I suppose. It somehow misses to capture the message field of the exception, whereas the default exception handler of the regular application server works as expected.


Solution

  • After opening a ticket for the issue, I was told that the error message in the body is taken care of by Spring Boot which configures error mappings at the Servlet container level and since Spring MVC Test runs with a mock Servlet request/response, there is no such error mapping. Further, they recommended me to create at least one @WebIntegrationTest and stick to Spring MVC Test for my controller logic.

    Eventually, I decided to go with my own custom exception handler and stick to MockMvc for the rest as before.

    @ControllerAdvice
    public class CustomExceptionHandler {
    
        @ExceptionHandler(Throwable.class)
        public @ResponseBody
        ExceptionResponse handle(HttpServletResponse response, Throwable throwable) {
            HttpStatus status = Optional
                    .ofNullable(AnnotationUtils.getAnnotation(throwable.getClass(), ResponseStatus.class))
                    .map(ResponseStatus::value)
                    .orElse(HttpStatus.INTERNAL_SERVER_ERROR);
            response.setStatus(status.value());
            return new ExceptionResponse(throwable.getMessage());
        }
    
    }
    
    @Data
    public class ExceptionResponse extends AbstractResponse {
    
        private final long timestamp = System.currentTimeMillis();
    
        private final String message;
    
        @JsonCreator
        public ExceptionResponse(String message) {
            checkNotNull(message, "message == NULL");
            this.message = message;
        }
    
    }