Spring MockMVC - 如何模拟在控制器外部运行的自定义验证器

时间:2014-04-18 20:25:59

标签: spring validation unit-testing spring-test spring-test-mvc

@UsernameAlreadyExists
private String username;

我创建了一个自定义验证程序,以确保在帐户创建表单提交时应用程序捕获重复的用户名。

当我使用MockMVC对帐户创建控制器进行单元测试时,由于验证器依赖于服务而失败,因此我得到空指针异常。

如何模拟验证器或验证器所依赖的服务? 我无法弄清楚如何使这项工作,因为控制器没有明确依赖验证器,它在控制器之外运行。

2 个答案:

答案 0 :(得分:3)

使用'standaloneSetup'进行测试时,如果没有加载整个Spring上下文,则验证由SpringWebConstraintValidatorFactory上的框架调用在内部触发。要连接到该流,您需要在'mockMvc'中设置SpringWebConstraintValidatorFactory的新实例。
不幸的是,这样做并不容易。您必须子类LocalValidatorFactoryBean并在LocalValidatorFactoryBean中设置您的实例,而后者又可以在mockMvc中设置。但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; } } 。这是一个例子:



    @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();
        }
    }

将其连接到mockMvc

的示例
ReflectionTestUtils.setField(constraintFactory, "isValid", false);

一旦拥有它,onDraw(Canvas canvas)将按预期工作,您可以通过工厂在验证器中设置字段。
查看问题背景spring-projects/spring-test-mvc/issues

答案 1 :(得分:2)

你不能模仿ConstraintValidator,但你肯定可以使用通常的模拟bean的弹簧方式来模拟验证器所依赖的服务,例如:

0.1。可能使用完全相同的bean名称定义一个模拟实例,确保在实际实例之后加载带有mock的配置。

0.2。仅使用仅定义了模拟bean的测试配置进行测试。