Spring MockMVC - How to mock custom validators running outside of controllers

前端 未结 2 870
暗喜
暗喜 2020-12-20 03:39
@UsernameAlreadyExists
private String username;

I have a custom validator that I created to ensure that duplicate usernames are caught by the appli

相关标签:
2条回答
  • 2020-12-20 04:20

    When testing with the use of 'standaloneSetup' that is without loading entire Spring context, validation is triggered internally by the framework call on SpringWebConstraintValidatorFactory. In order to connect to that flow you need to set new instance of SpringWebConstraintValidatorFactory in 'mockMvc'.
    Unfortunatelly there isn't an easy clean way of doing so. You have to subclass SpringWebConstraintValidatorFactory and set your instance in LocalValidatorFactoryBean which in turn can be set in mockMvc. But LocalValidatorFactoryBean will need ApplicationContext. Here is an example:

    
    
        public class TestConstrainValidationFactory extends SpringWebConstraintValidatorFactory {
    
            private final WebApplicationContext ctx;
    
            private boolean isValid = false;
    
            public TestConstrainValidationFactory(WebApplicationContext ctx) {
                this.ctx = ctx;
            }
    
            @Override
            public < T extends ConstraintValidator<?, ?>> T getInstance(Class key) {
                ConstraintValidator instance = super.getInstance(key);
                if (instance instanceof DeviceValidator) {
                    DeviceValidator deviceValidator = (DeviceValidator) instance;
                    deviceValidator.setYourAutowiredField((String id, String type) -> isValid); //change that to suit your needs
                    instance = deviceValidator;
                }
                return (T) instance;
            }
    
            @Override
            protected WebApplicationContext getWebApplicationContext() {
                return ctx;
            }
    
        }
    
    

    Example of connecting that to mockMvc

    
    
        @RunWith(SpringJUnit4ClassRunner.class)
        @SpringApplicationConfiguration(classes = MockServletContext.class)
        @WebAppConfiguration()
        public class DevicesControllerTest {
            @Autowired
            private MockServletContext servletContext;
            @InjectMocks
            private DevicesController devicesController;
            private TestConstrainValidationFactory constraintFactory;
    
            @Before
            public void setUp() {
                MockitoAnnotations.initMocks(this);
    
                final GenericWebApplicationContext context = new GenericWebApplicationContext(servletContext);
                final ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) context).getBeanFactory();
                beanFactory.registerSingleton(DeviceValidator.class.getCanonicalName(), new DeviceValidator());
                context.refresh();
    
                LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
                validatorFactoryBean.setApplicationContext(context);
                constraintFactory = new TestConstrainValidationFactory(context);
                validatorFactoryBean.setConstraintValidatorFactory(constraintFactory);
                validatorFactoryBean.setProviderClass(HibernateValidator.class);
                validatorFactoryBean.afterPropertiesSet();
    
                this.mockMvc = MockMvcBuilders
                        .standaloneSetup(devicesController)
                        .setValidator(validatorFactoryBean)
                        .setHandlerExceptionResolvers()
                        .build();
            }
        }
    
    

    Once you have it, ReflectionTestUtils.setField(constraintFactory, "isValid", false); will work as expected and you can set fields in the validator via the factory.
    View issue context spring-projects/spring-test-mvc/issues:

    0 讨论(0)
  • 2020-12-20 04:25

    You cannot mock the ConstraintValidator but you certainly can mock the service that the validator depends on, using the usual spring ways of mocking beans, for eg.:

    .1. Potentially define a mock instance with the exact same bean name, ensuring that your config with mock gets loaded after the real instance.

    .2. Test with only the test configuration with only the mock bean defined.

    0 讨论(0)
提交回复
热议问题