testing jsr303 annotated beans
I have been looking at using jsr303 annotations to mark up some of my beans. The good news is that both Hibernate and Spring play nicely with these annotations, and will validate them at appropriate times. The bad news is that the examples of testing these annotations that I have found online so far look cumbersome and awkward.
Let’s take a simple example:
public class SimpleBean {
@NotNull
private Long id;
private String name;
public SimpleBean(Long id, String name) {
this.id = id; this.name = name;
}
// getters and setters...
}
This is fine so far and we have the possibility that the constructor could be passed a null id. So, let’s write a unit test that ensures our validation will barf.
public class SimpleBeanTest {
private Validator validator;
@Before
public void setUp() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void nullIdMustThrowValidationError() {
SimpleBean bean = new SimpleBean(null, "valid string");
Set<ConstraintViolation<SimpleBean>> violations = validator.validate(bean);
assertThat(violations.size(), equalTo(1));
}
}
If this test fails, we get something like this as the result:
java.lang.AssertionError:
Expected: <1>
but: was <0>
...
Not so pretty. This is where hamcrest’s matcher toolkit comes in handy. What if we could write our test like this instead?
public class SimpleBeanTest {
@Test
public void nullIdMustThrowValdationError() {
SimpleBean bean = new SimpleBean(null, "valid string");
assertThat(bean, hasNoViolations());
}
}
A matcher implementation could look like this
public class JSR303NoViolationsMatcher<T> extends TypeSafeMatcher<T> {
private Validator validator;
private Set<ConstraintViolation<T>> violations;
public JSR303NoViolationsMatcher() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Override
protected boolean matchesSafely(T t) {
violations = validator.validate(t);
return violations.size() == 0;
}
@Override
public void describeTo(Description description) {
description.appendText("no jsr303 validation violations");
}
@Override
protected void describeMismatchSafely(Object item, Description mismatchDescription) {
mismatchDescription.appendText("was ").appendValue(violations);
}
@Factory
public static JSR303NoViolationsMatcher hasNoViolations() {
return new JSR303NoViolationsMatcher();
}
}
And our new-look test spits out the following upon failure:
java.lang.AssertionError:
Expected: no jsr303 validation violations
but: was <[ConstraintViolationImpl{interpolatedMessage='may not be null', propertyPath=id, rootBeanClass=class com.example.SimpleBean, messageTemplate='{javax.validation.constraints.NotNull.message}'}]>
There. Much nicer. Not only do we know that the test failed, but we are immediately told how it failed.
Thanks to planetgeek for a nice article about writing custom hamcrest matchers.