I have been told that testing java
exceptions' messages is a good thing, but it looks so much like redundancy that I cannot believe it.
Consider following fictional code:
public class PersonTooYoungException extends RuntimeException {
private static final String MESSAGE = "%d years old is much too young to register";
public PersonTooYoungException(Integer age) {
super(format(MESSAGE, age));
}
}
public class PersonValidator implements Validator<Person> {
private static final int MINIMAL_AGE = 18;
private Clock clock;
public PersonValidator(Clock clock) {
this.clock = clock;
}
@Override
public void validate(Person person) {
final int personAge = person.getAge(clock);
if (personAge < MINIMAL_AGE) {
throw new PersonTooYoungException(personAge);
}
}
}
and single test for it:
public class PersonValidatorTest {
PersonValidator personValidator;
@Before
public void setUp() throws Exception {
personValidator = new PersonValidator(new FixedClock(2016, JANUARY, 1));
}
@Test
public void shouldThrowExceptionForYoungerThan18() throws Exception {
// given
LocalDate birthDate = LocalDate.of(1998, FEBRUARY, 1);
Person person = new Person(birthDate, "Mike", "Trevin");
// when
Throwable exception = catchThrowable(() -> personValidator.validate(person));
// then
assertThat(exception)
.isInstanceOf(PersonTooYoungException.class)
.hasMessage("17 years old is much too young to register");
}
}
There are some issues with this code, but the thing that makes me sick is a redundancy of PersonTooYoungException.MESSAGE
.
This one could be solved by changing visibility of PersonTooYoungException.MESSAGE
to public
and then using format(MESSAGE, computedAge)
to compare. I don't like this one since it requires me to expose MESSAGE
just for test visibility.
Another solution is to check if exception message contains current age of person. In this case, it could probably pass, but there are other cases where exception message contains some important data (even more data) and simply calling contains
does not work right since it may catch some other values.
On the other hand, if we don't test the message at all, we won't be able to check the code which does some logic.
My question does not concern this particular example and this is not only about exceptions. Is there a standard way of handling/testing such and similar code? What about the cases where exception message contains more data (like some property name or value)?