Search code examples
javaunit-testingspringspring-mvcspring-annotations

Testing Spring @MVC annotations


I ran into a problem the other day where a @Valid annotation was accidentally removed from a controller class. Unfortunately, it didn't break any of our tests. None of our unit tests actually exercise the Spring AnnotationMethodHandlerAdapter pathway. We just test our controller classes directly.

How can I write a unit or integration test that will correctly fail if my @MVC annotations are wrong? Is there a way I can ask Spring to find and exercise the relevant controller with a MockHttpServlet or something?


Solution

  • In upcoming spring 3.2 (SNAPSHOT available) or with spring-test-mvc (https://github.com/SpringSource/spring-test-mvc) you can do it like this:

    first we emulate Validation as we do not want to test the validator, just want to know if validation is called.

    public class LocalValidatorFactoryBeanMock extends LocalValidatorFactoryBean
    {
        private boolean fakeErrors;
    
        public void fakeErrors ( )
        {
            this.fakeErrors = true;
        }
    
        @Override
        public boolean supports ( Class<?> clazz )
        {
            return true;
        }
    
        @Override
        public void validate ( Object target, Errors errors, Object... validationHints )
        {
            if (fakeErrors)
            {
                errors.reject("error");
            }
        }
    }
    

    this is our test class:

    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @ContextConfiguration
    public class RegisterControllerTest
    {
     @Autowired
     private WebApplicationContext  wac;
     private MockMvc mockMvc;
    
         @Autowired
         @InjectMocks
         private RegisterController registerController;
    
         @Autowired
         private LocalValidatorFactoryBeanMock  validator;
    
      @Before
      public void setup ( )
      {
         this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
         // if you want to inject mocks into your controller
                 MockitoAnnotations.initMocks(this);
      }
    
        @Test
        public void testPostValidationError ( ) throws Exception
        {
            validator.fakeErrors();
            MockHttpServletRequestBuilder post = post("/info/register");
            post.param("name", "Bob");
            ResultActions result = getMockMvc().perform(post);
                // no redirect as we have errors
            result.andExpect(view().name("info/register"));
        }
    
        @Configuration
        @Import(DispatcherServletConfig.class)
        static class Config extends WebMvcConfigurerAdapter
        {
            @Override
            public Validator getValidator ( )
            {
                return new LocalValidatorFactoryBeanMock();
            }
    
            @Bean
            RegisterController registerController ( )
            {
                return new RegisterController();
            }
        }
    }