@UsernameAlreadyExists
private String username;
我创建了一个自定义验证器,以确保在提交帐户创建表单时应用程序捕获重复的用户名。
当我使用 MockMVC 对帐户创建 Controller 进行单元测试时,由于验证器依赖于服务而失败,因此我得到了空指针异常。
如何模拟验证器或该验证器所依赖的服务? 我无法弄清楚如何使其工作,因为 Controller 不明确依赖于验证器,它在 Controller 外部运行。
最佳答案
使用“standaloneSetup”进行测试(无需加载整个 Spring 上下文)时,验证由 SpringWebConstraintValidatorFactory
上的框架调用在内部触发。为了连接到该流程,您需要在“mockMvc”中设置 SpringWebConstraintValidatorFactory
的新实例。
不幸的是,没有一种简单干净的方法可以做到这一点。您必须子类化 SpringWebConstraintValidatorFactory
并在 LocalValidatorFactoryBean
中设置实例,而该实例又可以在 mockMvc 中设置。但是 LocalValidatorFactoryBean
将需要 ApplicationContext
。这是一个例子:
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;
}
}
将其连接到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();
}
}
一旦获得它,ReflectionTestUtils.setField(constraintFactory, "isValid", false);
将按预期工作,您可以通过工厂在验证器中设置字段。
查看问题上下文 spring-projects/spring-test-mvc/issues :
关于Spring MockMVC - 如何模拟在 Controller 外部运行的自定义验证器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23161752/