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:
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…