I'm wondering if there is a clean and complete way to assert on the message attached to a thrown exception when that message was generated using String.format(). For example, a class like:
public class Car {
public static final String DRIVE_ERROR = "Can't drive while car %s is parked!";
private String name;
private boolean parked;
public Car(String name) {
this.name = name;
this.parked = true;
}
public void drive() {
if (parked) {
throw new IllegalStateException(String.format(DRIVE_ERROR, name));
}
}
}
(Sorry for the weird example, just trying to keep it as simple as possible) Now if I were testing the car, I'd have a class like this:
public class CarTest {
@Test
public void drive_test() {
Car car = new Car("Greased Lightning");
assertThatThrownBy(() -> car.drive())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("???");
}
}
The question is, what is the best way to assert on the message? In this example, I could separate out the declaration of the name of the car, then use String format myself to grab the static string from Car and format in the name, but that seems like a lot of extra code, and can't be easily used in a lot of instances (eg. when the item that goes in the formatted string is determined at runtime). What I'd really like to be able to do is pass the error message string to hasMessageContaining
and have it ignore the "%s" placeholder and accept anything in that spot. Is there a way to do regex matching of Strings with assertJ? Or some other way of doing this cleanly?
EDIT: I'm also open to alternatives on throwing exceptions that have messages that are easier to test. One solution is just using String concatenation, like throw new Exception(STATIC_ERROR_MESSAGE + name)
and then testing that the message contains the first part, but that really limits your message formatting ability and doesn't look very clean.
Exception message assertions are limited compared to regular String assertion.
What you could do is use matches
or containsPattern
assertions, ex:
@Test
public void test() {
// GIVEN some preconditions
// WHEN
Throwable thrown = catchThrowableOfType(() -> { throw new IllegalStateException("boom!"); },
IllegalStateException.class);
// THEN
assertThat(thrown.getMessage()).matches(".oo.")
.containsPattern("oo.");
// or even better thanks to Rolland Illig suggestion
assertThat(thrown).hasMessageMatching(".oo.");
}
Note that by using catchThrowableOfType
you don't have to check that the caught exception is of the expected type anymore.