The Hibernate Validator documentation has a simple Getting Started guide which outlines testing validation rules.

The relevant chunk is

@Test
public void manufacturerIsNull() {
    Car car = new Car(null, "DD-AB-123", 4);

    Set<ConstraintViolation<Car>> constraintViolations =
        validator.validate(car);

    assertEquals(1, constraintViolations.size());
    assertEquals("may not be null", constraintViolations.iterator().next().getMessage());
}

It seems to me a fairly vague route to test if the NotNull constraint has been violated.

My simple solution would be something like

public static <T> boolean  containsConstraintViolation(
    Set<ConstraintViolation<T>> violations, Class<?> constraint) {

    for (ConstraintViolation<?> violation : violations) {
        ConstraintDescriptor<?> descriptor = violation.getConstraintDescriptor();
            if (constraint.isAssignableFrom(descriptor.getAnnotation().getClass()))
                return true;
        }
    return false;
}

Which allows me to do tests like

assertTrue(ValidationUtils.containsConstraintViolation(violations, NotNull.class));

However, I'm sure this is going to be naive in the long run and am wondering if there's not some other library or API I'm missing to assist in unit testing constraints.

有帮助吗?

解决方案

You could have a look at the class org.hibernate.validator.test.util.TestUtil which is used for Hibernate Validator's own tests and offers functionality for testing expected constraint violations (amongst others there is assertCorrectConstraintTypes() for example).

Just note, that this class is not part of Hibernate Validator's public API, so it may be best to just use it to get some ideas.

When comparing error messages always be sure to have the VM's locale correctly set. Even better is to match against localized messages loaded via the correct resource bundle (org.hibernate.validator.ValidationMessages for the standard constraints in the case of Hibernate Validator).

其他提示

Testing the bean validation against a concrete constraint class is a bad idea because will tightly couple the test with the implementation. So, instead of this:

assertTrue(ValidationUtils.containsConstraintViolation(violations, NotNull.class));

You could test the outcome of the validation to be that of what you expect:

assertThat(validationFor(car, onField("manufacturer")), fails());

So you tests will become like this

@Test
public void car_with_null_manufacturer_is_invalid() {
    Car car = new Car(null, "DD-AB-123", 4);

    assertThat(validationFor(car, onField("manufacturer")), fails());
}

The utility class that provides the matchers is

public class HibernateValidationUtils {
    private static Validator VALIDATOR;
    static {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        VALIDATOR = factory.getValidator();
    }

    public static Set<ConstraintViolation<Object>> validationFor(Object object, String fieldname) {
        return VALIDATOR.validateProperty(object, fieldname);
    }

    public static String onField(String fieldname) {
        return fieldname;
    }

    public static Matcher<Set<ConstraintViolation<Object>>> succedes() {
        return new PassesValidation();
    }

    public static Matcher<Set<ConstraintViolation<Object>>> fails() {
        return new Not(new PassesValidation());
    }

    static class PassesValidation extends BaseMatcher<Set<ConstraintViolation<Object>>> {
        @Override
        public boolean matches(Object o) {
            boolean result = false;
            if (o instanceof Set) {
                result = ((Set) o).isEmpty();
            }
            return result;
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("valid");
        }
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top