Im writing tests for a controller that throws a custom exception (AuthenticationException
in my case) which is annotated with @ResponseStatus(value = HttpStatus.BAD_REQUEST)
Calling the endpoint that throws the exception using i.e. curl works fine and i get the expected outcome with an example shown below:
{
"timestamp": 1494185397677,
"status": 400,
"error": "Bad Request",
"exception": "com.example.exception.AuthenticationException",
"message": "These are not the credentials you are looking for",
"path": "/status/throw/2"
}
When i write a test for this with Mockito which uses willThrow()
i dont get any of the output that Spring Boot generates but just the response code as annotated in my exception class.
Here is my test:
@Test
public void throwsShouldShowResponseBody() throws Exception {
given(this.service.getAccStatus())
.willThrow(AuthenticationException.class);
this.mvc.perform(get("/status/throw/2"))
.andExpect(status().isBadRequest())
.andDo(document("bad-credentials"));
}
Looking at similar questions it seems that this might be caused by MockMvc not following redirects which i think Spring Boot is using to push to /error but my ask is if there is anyway i can make this work so i dont have to write @ControllerAdvice
class with something similar to ErrorAttributes
that Spring Boot already provides. I dont wish to change the output that Spring Boot generates on an error.
Thanks -
As you've noted, it's a little bit tricky to document Spring Boot's error response when using MockMvc
. This is because Spring Boot forwards the request to the error controller that's mapped to /error
and MockMvc
doesn't process forwards by default.
One way to document an error response is to call /error
directly with an appropriately configured request. There's an example of this in one of Spring REST Docs' samples:
@Test
public void errorExample() throws Exception {
this.mockMvc
.perform(get("/error")
.requestAttr(RequestDispatcher.ERROR_STATUS_CODE, 400)
.requestAttr(RequestDispatcher.ERROR_REQUEST_URI, "/notes")
.requestAttr(RequestDispatcher.ERROR_MESSAGE, "The tag 'http://localhost:8080/tags/123' does not exist"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("error", is("Bad Request")))
.andExpect(jsonPath("timestamp", is(notNullValue())))
.andExpect(jsonPath("status", is(400)))
.andExpect(jsonPath("path", is(notNullValue())))
.andDo(this.documentationHandler.document(
responseFields(
fieldWithPath("error").description("The HTTP error that occurred, e.g. `Bad Request`"),
fieldWithPath("message").description("A description of the cause of the error"),
fieldWithPath("path").description("The path to which the request was made"),
fieldWithPath("status").description("The HTTP status code, e.g. `400`"),
fieldWithPath("timestamp").description("The time, in milliseconds, at which the error occurred"))));
}
It's then used in the resulting documentation to describe the error response format that's used across the entire API.