使用自动弹簧服务测试自定义验证器

ren*_*rof 7 testing validation spring hibernate-validator

我的实体有一个自定义的Hibernate验证器.我的一个验证器使用Autowired Spring @Repository.应用程序正常工作,我的存储库在我的验证器上成功自动装配.

问题是我找不到测试验证器的方法,因为我无法在其中注入我的存储库.

Person.class:

@Entity
@Table(schema = "dbo", name = "Person")
@PersonNameMustBeUnique
public class Person {

    @Id
    @GeneratedValue
    @Column(name = "id", unique = true, nullable = false)
    private Integer id;

    @Column()
    @NotBlank()
    private String name;

    //getters and setters
    //...
}
Run Code Online (Sandbox Code Playgroud)

PersonNameMustBeUnique.class

@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { PersonNameMustBeUniqueValidator.class })
@Documented
public @interface PersonNameMustBeUnique{
    String message() default "";

    Class<?>[] groups() default {};

    Class<? extends javax.validation.Payload>[] payload() default {};
}
Run Code Online (Sandbox Code Playgroud)

验证者:

public class PersonNameMustBeUniqueValidatorimplements ConstraintValidator<PersonNameMustBeUnique, Person> {

    @Autowired
    private PersonRepository repository;

    @Override
    public void initialize(PersonNameMustBeUnique constraintAnnotation) { }

    @Override
    public boolean isValid(Person entidade, ConstraintValidatorContext context) {
        if ( entidade == null ) {
            return true;
        }

        context.disableDefaultConstraintViolation();

        boolean isValid = nameMustBeUnique(entidade, context);

        return isValid;
    }

    private boolean nameMustBeUnique(Person entidade, ConstraintValidatorContext context) {
        //CALL REPOSITORY TO CHECK IF THE NAME IS UNIQUE 
        //ADD errors if not unique...
    }
}
Run Code Online (Sandbox Code Playgroud)

上下文文件有一个验证器bean:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
Run Code Online (Sandbox Code Playgroud)

再次,它工作正常,但我不知道如何测试它.

我的测试文件是:

@RunWith(MockitoJUnitRunner.class)
public class PersonTest {

    Person e;
    static Validator validator;

    @BeforeClass
    public static void setUpClass() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    @Test
    public void name__must_not_be_null() {
        e = new Person();
        e.setName(null);
        Set<ConstraintViolation<Person>> violations = validator.validate(e);
        assertViolacao(violations, "name", "Name must not be null");
    }

}
Run Code Online (Sandbox Code Playgroud)

Rad*_*xca 8

我面临着非常相似的问题:如何编写具有自动装配配置 bean 的自定义验证器的纯单元测试?

我可以通过以下代码设法解决它(灵感来自用户abhishekrvce 的这个答案)。

这是带有@Autowired 配置bean 的自定义验证器的纯单元测试,它从配置文件(未在代码中显示)读取数据。

@Import({MyValidator.class})
@ContextConfiguration(classes = MyConfiguration.class, initializers = ConfigFileApplicationContextInitializer.class)
class MyValidatorTest {

  private LocalValidatorFactoryBean validator;

  @Autowired
  private ConfigurableApplicationContext applicationContext;

  @BeforeEach
  void initialize() {
    SpringConstraintValidatorFactory springConstraintValidatorFactory
        = new SpringConstraintValidatorFactory(
        applicationContext.getAutowireCapableBeanFactory());
    validator = new LocalValidatorFactoryBean();
    validator.setConstraintValidatorFactory(springConstraintValidatorFactory);
    validator.setApplicationContext(applicationContext);
    validator.afterPropertiesSet();
  }

  @Test
  void isValid()
  {
    Set<ConstraintViolation<MyObject>> constraintViolations = validator
        .validate(myObjectInstance);
    assertThat(constraintViolations).hasSize(1);
  }

}
Run Code Online (Sandbox Code Playgroud)


ren*_*rof 6

在@BeforeClass 上:

@BeforeClass
    public static void setUpClass() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }
Run Code Online (Sandbox Code Playgroud)

在你的测试中,你需要用你的模拟 bean 替换 bean:

myValidator.initialize(null);
BeanValidatorTestUtils.replaceValidatorInContext(validator, usuarioValidoValidator, e);
Run Code Online (Sandbox Code Playgroud)

做所有魔法的类:

public class BeanValidatorTestUtils {

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static <A extends Annotation, E> void replaceValidatorInContext(Validator validator,
                                                                            final ConstraintValidator<A, ?> validatorInstance,
                                                                                E instanceToBeValidated) {
        final Class<A> anotacaoDoValidador = (Class<A>)
                                                ((ParameterizedType) validatorInstance.getClass().getGenericInterfaces()[0])
                                                    .getActualTypeArguments()[0];

        ValidationContextBuilder valCtxBuilder = ReflectionTestUtils.<ValidationContextBuilder>invokeMethod(validator,
                                                                                                "getValidationContext");
        ValidationContext<E> validationContext = valCtxBuilder.forValidate(instanceToBeValidated);
        ConstraintValidatorManager constraintValidatorManager = validationContext.getConstraintValidatorManager();

        final ConcurrentHashMap nonSpyHashMap = new ConcurrentHashMap();
        ConcurrentHashMap spyHashMap = spy(nonSpyHashMap);
        doAnswer(new Answer<Object>() {
            @Override public Object answer(InvocationOnMock invocation) throws Throwable {
                Object key = invocation.getArguments()[0];
                Object keyAnnotation = ReflectionTestUtils.getField(key, "annotation");
                if (anotacaoDoValidador.isInstance(keyAnnotation)) {
                    return validatorInstance;
                }
                return nonSpyHashMap.get(key);
            }
        }).when(spyHashMap).get(any());

        ReflectionTestUtils.setField(constraintValidatorManager, "constraintValidatorCache", spyHashMap);
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 这是什么魔法? (6认同)
  • 反射宝贝;) (2认同)

小智 6

您可以在测试中将以下 bean 添加到您的 Spring Context 中:

@RunWith(SpringRunner.class)
@Import(LocalValidatorFactoryBean.class)
public class PersonTest {

  @Autowired
  private Validator validator;

  {
    validator.validate(new Person());
  }

  ...
}
Run Code Online (Sandbox Code Playgroud)