Testing hibernate validators

Julio Santana
2 min readMar 27, 2023

Testing is an essential part of the development life cycle. Being able to test your whole functionality from Unit testing to E2E, choosing the right place to put the right tests as suggested by the testing pyramid, the right number of tests, the right input that cover corner cases and turning points, is a skill that every software engineer should have on its toolbox.

To do this, I usually disect my functionality into layers and decide what test will cover what part. Some of them are evident, every function must have a unit test, every endpoint an integretion one. But then I get to some other complicated parts, like how to be sure a given configuration is correct (Ex. if the property referring to a url of an external service is not correctly declared in your application.yaml, you application will fail at runtime even when your logic is perfect), or how to test part of the code that were expressed declaratively (the ones using annotations), are among the most challenging test to create for me.

Test layers splitting

Precisely, hibernate validators fall on this category. They are a declarative way of making sure that the model of the data you are dealing with is correct. You annotate entities with things like @NotNull or @Max(10) to put constraints into your fields that the people talking to your code must follow. But how to be sure this annotations are in the correctly configured, after all they being declarative means that they only say what they do, but “how they work” is hidden for us.

The object used to do this is a Validator, and when invoking its validate method with an annotated entity with values, it will tell us the number of violations it finds on these values. Given this, we could have a base class in our tests were we receive an entity with 1 or more failures like this.

public abstract class ValidatorTest<T> {

private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

@MethodSource("invalidDtos")
@ParameterizedTest
void allValidatorsAreCorrectlySet(T dto) {
var constraintViolations = validator.validate(dto);
assertEquals(constraintViolations.size(), 1);
}
}

Then in the tests that we want to write for every entity we could inherit from this class, and simply pass to the previous method entities that have exactly 1 field that doesn’t pass the validation to make sure they are being applied to them and 1 error will be reported.

class PersonDtoTest extends ValidatorTest<PersonDto> {

private static Stream<Arguments> invalidEvents() {
return Stream.of(
Arguments.of(givenPersonWithNullID()),
Arguments.of(givenPersonWithEmptyName())
);
}

public static PersonDto givenPersonWithNullID() {
return new PersonDto(null, "Julio");
}

public static PersonDto givenPersonWithEmptyName() {
return new PersonDto(1, "");
}

}

and in this simple way, we have a “framework” ready to test all Dtos in our model are annotated as expected and are valid.

--

--