testngspring-testmockmvcspring-test-mvc

How to enable controller parameter validation for standalone MockMvc


Controller

@RestController
@Validated
class MyController {

    @GetMapping("/foo")
    public String unwrapped(@Min(1) @RequestParam("param") int param) {
        return Integer.toString(param);
    }

    @GetMapping("/bar")
    public String wrapped(@ModelAttribute @Valid Wrapper bean) {
        return Integer.toString(bean.param);
    }

    static class Wrapper {

        @Min(1)
        int param;

        public void setParam(final int param) {
            this.param = param;
        }
    }
}

Test

public class MyControllerTest {

    MyController controller = new MyController();
    MockMvc mockMvc = MockMvcBuilders
            .standaloneSetup(this.controller)
            .build();

    @Test // fails
    public void unwrapped() throws Exception {
        this.mockMvc.perform(get("/foo")
                .param("param", "0"))
                .andExpect(status().isBadRequest());
    }

    @Test // passes
    public void wrapped() throws Exception {
        this.mockMvc.perform(get("/bar")
                .param("param", "0"))
                .andExpect(status().isBadRequest());
    }
}

To enable (unwrapped) method parameter validation in spring the controller has to be annotated with @Validated and the MethodValidationPostProcessor must be added to the context.
Is it possible to add the MethodValidationPostProcessor bean to the standalone setup ?
The question might be reduced to how to add a BeanPostProcessor to a standalone MockMvc setup ?


Solution

  • Is it possible to add the MethodValidationPostProcessor bean to the standalone setup ?

    No, that is unfortunately not possible when using MockMvcBuilders.standaloneSetup() since that creates a StubWebApplicationContext behind the scenes which does not support BeanPostProcessors.

    Once SPR-6380 has been addressed (currently in the Spring Framework 5.x backlog), you will no longer need to rely on the MethodValidationPostProcessor. However, in the interim you will need to switch to the use of MockMvcBuilders.webAppContextSetup() in order to be able to test the behavior added to you controller via MethodValidationPostProcessor.

    Original Erroneous Suggestion

    I don't think it's possible to do that directly; however, you could create a custom subclass of StandaloneMockMvcBuilder that overrides the initWebAppContext() method, delegates to super.initWebAppContext() to create the WebApplicationContext, and then registers the MethodValidationPostProcessor bean programmatically via org.springframework.test.web.servlet.setup.StubWebApplicationContext.addBean(String, Object).