Skip to content

Standalone MockMvc ignores @RestControllerAdvice annotation attributes #25520

Closed
@yevhenii-pazii

Description

@yevhenii-pazii

MockMvc created by MockMvcBuilders.standaloneSetup() ignores @RestControllerAdvice annotation attributes but works well for a @ControllerAdvice/ResponseBody pair.

  • Spring Boot: 2.3.2
  • Spring Framework: 5.2.8

Example: https://github.com/thecederick/MockMvc-ignores-RestControllerAdvice-annotation-fields

Failing Configuration

Controller:

@RestController
@RequestMapping
public class Api1Controller {

    @PostMapping(value = "/endpoint1")
    public String endpoint() {
        return "done";
    }
}

Controller advice:

@RestControllerAdvice(assignableTypes = Api1Controller.class)
@Order(Ordered.HIGHEST_PRECEDENCE)
public class Api1ControllerAdvice {

    @ExceptionHandler(Throwable.class)
    public String handleException(Throwable throwable) {
        return this.getClass() + " - " + throwable.getClass();
    }
}

Test:

    @BeforeEach
    public void setup() {
        this.mockMvc = MockMvcBuilders
                .standaloneSetup(controller)
                .addDispatcherServletCustomizer(
                        dispatcherServlet -> dispatcherServlet.setThrowExceptionIfNoHandlerFound(true))
                .setControllerAdvice(Api1ControllerAdvice.class, DefaultControllerAdvice.class)
                .build();
    }

    @Test
    void notFound() throws Exception {
        mockMvc
                .perform(
                        post("/test")
                                .contentType("application/json")
                                .content("{}"))
                .andExpect(content().string(
                        "class com.example.demo.root.DefaultControllerAdvice - class org.springframework.web.servlet.NoHandlerFoundException"));
    }

Working Configuration

@RestController
@RequestMapping
public class Api2Controller {

    @PostMapping(value = "/endpoint2")
    public String endpoint() {
        return "done";
    }
}

Controller advice:

@ControllerAdvice(assignableTypes = Api2Controller.class)
@ResponseBody
@Order(Ordered.HIGHEST_PRECEDENCE)
public class Api2ControllerAdvice {

    @ExceptionHandler(Throwable.class)
    public String handleException(Throwable throwable) {
        return this.getClass() + " - " + throwable.getClass();
    }
}

Test:

    @BeforeEach
    public void setup() {
        this.mockMvc = MockMvcBuilders
                .standaloneSetup(controller)
                .addDispatcherServletCustomizer(
                        dispatcherServlet -> dispatcherServlet.setThrowExceptionIfNoHandlerFound(true))
                .setControllerAdvice(
                        Api2ControllerAdvice.class,
                        DefaultControllerAdvice.class)
                .build();
    }

    @Test
    void notFound() throws Exception {
        mockMvc
                .perform(
                        post("/test")
                                .contentType("application/json")
                                .content("{}"))
                .andExpect(content().string(
                        "class com.example.demo.root.DefaultControllerAdvice - class org.springframework.web.servlet.NoHandlerFoundException"));
    }

Default advice

@RestControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
public class DefaultControllerAdvice {

    @ExceptionHandler(Throwable.class)
    public String handleException(Throwable throwable) {
        return this.getClass() + " - " + throwable.getClass();
    }
}

Summary

In the first configuration using the @RestControllerAdvice annotation the test fails; with the second one, it passes as expected.

Metadata

Metadata

Assignees

Labels

in: testIssues in the test modulein: webIssues in web modules (web, webmvc, webflux, websocket)type: regressionA bug that is also a regression

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions