2

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 -

2
  • What's the connection with Spring REST Docs? Are you trying to document an error response? If not, you're really just testing that Spring Boot's error controller works rather than testing your application Commented May 9, 2017 at 20:38
  • i should have made that more clear; yes, im trying to document how spring boots error controller would output when i throw an exception or just when it returns a simple 404. Essentially i wanted a section in my documentation that would show spring boots error response that i could use as a reference. Commented May 9, 2017 at 20:42

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you - this is exactly what i was after! Also the samples you reference - i had never come across those had you not linked to them here. It would be nice if they where linked in the reference documentation of Spring REST Docs if they aren't already.
0
package com.cts.skynews.springboot.controller;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(UserControllerTest.class);

    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    public void addNewUser() throws Exception {
        LOGGER.info("START : Inside Spring Boot addUser() method of UserController");

        String USER_DATA = "{\"name\":\"Kiran Ravariya\"," + "\"email\":\"[email protected]\","
                + "\"password\":\"A123456\"," + "\"status\":\"active\"," + "\"language\":{\"id\":\"1\"},"
                + "\"role\":{\"id\":1}}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().isOk()).andExpect(jsonPath("$.signedUp").value("true"));

        LOGGER.info("END : Spring Boot addUser() method of UserController");

    }

    @Test
    public void checkEmailExists() throws Exception {
        LOGGER.info("START : Inside Spring Boot checkEmailExists() method of UserController");

        String USER_DATA = "{\"name\":\"Kiran Ravariya\"," + "\"email\":\"[email protected]\","
                + "\"password\":\"A123456\"," + "\"status\":\"active\"," + "\"language\":{\"id\":\"1\"},"
                + "\"role\":{\"id\":1}}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().isOk()).andExpect(jsonPath("$.emailExists").value("true"))
                .andExpect(jsonPath("$.signedUp").value("false"));

        LOGGER.info("END : Spring Boot checkEmailExists() method of UserController");

    }

    @Test
    public void incorrectEmailFormat() throws Exception {
        LOGGER.info("START : Inside Spring Boot incorrectEmailFormat() method of UserController");

        String USER_DATA = "{\"name\":\"Kiran Ravariya\"," + "\"email\":\"rgmail.com\"," + "\"password\":\"A123456\","
                + "\"status\":\"active\"," + "\"language\":{\"id\":\"1\"}," + "\"role\":{\"id\":1}}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().is4xxClientError())
                .andExpect(jsonPath("$.errorMessage").value("Input Validation Failed:Email address is invalid"));
        LOGGER.info("END : Spring Boot incorrectEmailFormat() method of UserController");
    }

    @Test
    public void nullName() throws Exception {
        LOGGER.info("START : Inside Spring Boot nullName() method of UserController");

        String USER_DATA = "{\"email\":\"[email protected]\"," + "\"password\":\"A123456\"," + "\"status\":\"active\","
                + "\"language\":{\"id\":\"1\"}," + "\"role\":{\"id\":1}}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().is4xxClientError())
                .andExpect(jsonPath("$.errorMessage").value("Input Validation Failed:Name cannot be empty"));

        LOGGER.info("END : Spring Boot nullName() method of UserController");
    }

    @Test
    public void nullPassword() throws Exception {
        LOGGER.info("START : Inside Spring Boot nullPassword() method of UserController");

        String USER_DATA = "{\"name\":\"Kiran Ravariya\"," + "\"email\":\"[email protected]\","
                + "\"status\":\"active\"," + "\"language\":{\"id\":\"1\"}," + "\"role\":{\"id\":1}}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().is4xxClientError())
                .andExpect(jsonPath("$.errorMessage").value("Input Validation Failed:Password cannot be empty"));

        LOGGER.info("END : Spring Boot nullPassword() method of UserController");
    }

    @Test
    public void nullEmail() throws Exception {
        LOGGER.info("START : Inside Spring Boot nullEmail() method of UserController");

        String USER_DATA = "{\"name\":\"Kiran Ravariya\"," + "\"password\":\"A123456\"," + "\"status\":\"active\","
                + "\"language\":{\"id\":\"1\"}," + "\"role\":{\"id\":1}}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().is4xxClientError())
                .andExpect(jsonPath("$.errorMessage").value("Input Validation Failed:Email cannot be empty"));

        LOGGER.info("END : Spring Boot nullEmail() method of UserController");
    }

    @Test
    public void nullStatus() throws Exception {
        LOGGER.info("START : Inside Spring Boot nullEmail() method of UserController");

        String USER_DATA = "{\"name\":\"Kiran Ravariya\"," + "\"email\":\"[email protected]\","
                + "\"password\":\"A123456\"," + "\"language\":{\"id\":\"1\"}," + "\"role\":{\"id\":1}}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().is4xxClientError())
                .andExpect(jsonPath("$.errorMessage").value("Input Validation Failed:Status cannot be empty"));

        LOGGER.info("END : Spring Boot nullStatus() method of UserController");
    }

    @Test
    public void langugaeNull() throws Exception {
        LOGGER.info("START : Inside Spring Boot langugaeNull() method of UserController");

        String USER_DATA = "{\"name\":\"Kiran Ravariya\"," + "\"email\":\"[email protected]\","
                + "\"password\":\"A123456\"," + "\"status\":\"active\"," + "\"role\":{\"id\":1}}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().is4xxClientError())
                .andExpect(jsonPath("$.errorMessage").value("Input Validation Failed:Language cannot be empty"));

        LOGGER.info("END : Spring Boot langugaeNull() method of UserController");
    }

    @Test
    public void roleNull() throws Exception {
        LOGGER.info("START : Inside Spring Boot roleNull() method of UserController");

        String USER_DATA = "{\"name\":\"Kiran Ravariya\"," + "\"email\":\"[email protected]\","
                + "\"password\":\"A123456\"," + "\"status\":\"active\"," + "\"language\":{\"id\":\"1\"}}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().is4xxClientError())
                .andExpect(jsonPath("$.errorMessage").value("Input Validation Failed:Role cannot be empty"));

        LOGGER.info("END : Spring Boot roleNull() method of UserController");
    }

    @Test
    public void invalidNameLength() throws Exception {
        LOGGER.info("START : Inside Spring Boot invalidNameLength() method of UserController");

        String USER_DATA = "{\"name\":\"KiranKiranRavariyKiranKiranRavariyaRRavariyaRavariyaRavariyaRavariyaRavariya "
                + "KiranKiranRavariyKiranKiranRavariyaRRavariyaRavariyaRavariyaRavariyaRavariya "
                + "KiranKiranRavariyKiranKiranRavariyaRRavariyaRavariyaRavariyaRavariyaRavariya\","
                + "\"email\":\"[email protected]\"," + "\"password\":\"A123456\"," + "\"status\":\"active\","
                + "\"language\":{\"id\":\"1\"}," + "\"role\":{\"id\":1}}";

        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().is4xxClientError())
                .andExpect(jsonPath("$.errorMessage").value("Input Validation Failed:Name must be 3 to 80 characters"));

        LOGGER.info("END : Spring Boot invalidNameLength() method of UserController");

    }

    @Test
    public void invalidEmailLength() throws Exception {
        LOGGER.info("START : Inside Spring Boot invalidEmailLength() method of UserController");

        String USER_DATA = "{\"name\":\"Kiran Ravariya\","
                + "\"email\":\"ravariakiran@gmailravariakiran@gmailravariakiran@gmailravariakiran@gmailravariakiran@gmailravariakiran@gmailravariakiran@gmailravariakiran@gmailravariakiran@[email protected]\","
                + "\"password\":\"A123456\"," + "\"status\":\"active\"," + "\"language\":{\"id\":\"1\"},"
                + "\"role\":{\"id\":1}}";

        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().is4xxClientError()).andExpect(
                        jsonPath("$.errorMessage").value("Input Validation Failed:Email must be 4 to 80 characters"));

        LOGGER.info("END : Spring Boot invalidEmailLength() method of UserController");

    }

    @Test
    public void incorrectPasswordFormat() throws Exception {
        LOGGER.info("START : Inside Spring Boot incorrectPasswordFormat() method of UserController");

        String USER_DATA = "{\"name\":\"Kiran Ravariya\"," + "\"email\":\"[email protected]\"," + "\"password\":\"12\","
                + "\"status\":\"active\"," + "\"language\":{\"id\":\"1\"}," + "\"role\":{\"id\":1}}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/signup").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().is4xxClientError()).andExpect(jsonPath("$.errorMessage")
                        .value("Input Validation Failed:Password must be 6 to 45 characters"));

        LOGGER.info("END : Spring Boot incorrectPasswordFormat() method of UserController");
    }

    @Test
    public void successfullLogin() throws Exception {
        LOGGER.info("START : Inside Spring Boot successfullLogin() method of UserController");

        String USER_DATA = "{\"email\":\"[email protected]\",\"password\":\"A123456\"}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/login").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().isOk()).andExpect(jsonPath("$.authenticated").value("true"));

        LOGGER.info("END : Spring Boot successfullLogin() method of UserController");
    }

    @Test
    public void invalidEmailForLogin() throws Exception {
        LOGGER.info("START : Inside Spring Boot invalidEmailForLogin() method of UserController");

        String USER_DATA = "{\"email\":\"[email protected]\",\"password\":\"A123456\"}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/login").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().isOk()).andExpect(jsonPath("$.authenticated").value("false"));

        LOGGER.info("END : Spring Boot invalidEmailForLogin() method of UserController");
    }

    @Test
    public void invalidPasswordForLogin() throws Exception {
        LOGGER.info("START : Inside Spring Boot invalidPasswordForLogin() method of UserController");

        String USER_DATA = "{\"email\":\"[email protected]\",\"password\":\"12345678\"}";
        LOGGER.debug("JSON Object :  {}", USER_DATA);

        mockMvc.perform(post("/user/login").content(USER_DATA).contentType("application/json;charset=UTF-8"))
                .andExpect(status().isOk()).andExpect(jsonPath("$.authenticated").value("false"));;

        LOGGER.info("END : Spring Boot invalidPasswordForLogin() method of UserController");
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.