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

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

问题描述:

我为我的实体使用了自定义的休眠验证器.我的验证者之一使用了自动装配的Spring @Repository.该应用程序运行正常,并且我的存储库已成功在验证程序上自动连线.

I have a custom Hibernate Validator for my entities. One of my validators uses an Autowired Spring @Repository. The application works fine and my repository is Autowired successfully on my validator.

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

The problem is i can't find a way to test my validator, cause i can't inject my repository inside it.

Person.class:

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
    //...
}

PersonNameMustBeUnique.class

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 {};
}

验证者:

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...
    }
}

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

And the context file has a validator bean:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

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

Again, it works fine, but i don't know how to test it.

我的测试文件是:

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

}

在@BeforeClass:

On @BeforeClass:

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

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

And in your test you need to replace the beans with your mocked bean:

myValidator.initialize(null);
BeanValidatorTestUtils.replaceValidatorInContext(validator, usuarioValidoValidator, e);

可以发挥所有魔力的类:

The class that do all the magic:

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

}