How to test a Validator which implements ConstraintValidator in java?

goe picture goe · Feb 27, 2015 · Viewed 14.6k times · Source

I have an "AllowedValuesValidator.java" class:

public class AllowedValuesValidator implements ConstraintValidator<AllowedValues, String> {

    String[] values;
    String defaultValue;

    @Override
    public void initialize(AllowedValues constraintAnnotation) {
        values = constraintAnnotation.allowedValues();
        defaultValue = constraintAnnotation.defaultValue();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (!StringUtils.isEmpty(defaultValue) && StringUtils.isEmpty(value)) {
            value = defaultValue;
        }

        if (!StringUtils.isEmpty(value) && !Arrays.asList(values).contains(value)) {
            return false;
        }
        return true;
    }
}

And the corresponding interface class:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = AllowedValuesValidator.class)
public @interface AllowedValues {

    String message();

    String fieldName();

    int fieldNumber();

    String[] allowedValues() default {"Y", "N"};

    String defaultValue() default "";
}

I want to be able to write a unit test class to test the direct logic in that validator. But it seems that most places I googled give examples of test classes where we basically test all validators for a given Model class, for example:

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

    @Test
    public void testEmailExistsIncorrect() {

        Set<constraintviolation<usercredentialsdto>> violations = validator
                .validate(credentials, UserCredentialsDto.class);
        Assert.assertEquals(1, violations.size());
    }

I don't want to build mock models to test all validators. Is there a way to create a separate test class for just testing the logic in one single validator directly without using any other model classes etc?

Answer

Hardy picture Hardy · Mar 3, 2015

You can test the validator standalone. The rub is of course the initialize method, since it needs an instance of the annotation. You basically have three options:

  1. Add a second initialize method which takes the required parameters directly. You can then use this method to initialize the validator. You can also make this method just package visible, provided your test resides in the same package
  2. Place the test annotation somewhere into your test class and retrieve it via reflection in order to pass it to the initialize method.
  3. Use annotation proxies. This is also what Hibernate Validator itself uses internally for in case constraints are configured via XML or needed for tests. There are two classes in Hibernate Validator which you could use AnnotationDescriptor and AnnotationFactory. The code would somewhat like this:

--

private AllowedValues createAnnotation(String[]values, String defaultValue) {
  AnnotationDescriptor<AllowedValues> descriptor = new AnnotationDescriptor<AllowedValues>( AllowedValues.class );
  descriptor.setValue( "values", values );
  descriptor.setValue( "defaultValue", defaultValue );

  return AnnotationFactory.create( descriptor );
}

You would need to depend on Hibernate Validator internal classes, but for testing purposes this should be fine. Of course you could also just create your own proxy framework.