Can you add a custom message to AssertJ assertThat?

And in classic fashion, I found what I was looking for moments after posting the question. Hopefully this will make it easier for the next person to find without first having to know what it's called. The magic method is the deceptively short-named as, which is part of another interface that AbstractAssert implements: Descriptable, not the base Assert interface.

public S as(String description, Object... args)

Sets the description of this object supporting String.format(String, Object...) syntax.
Example :

try {
  // set a bad age to Mr Frodo which is really 33 years old.
  frodo.setAge(50);
  // you can specify a test description with as() method or describedAs(), it supports String format args
  assertThat(frodo.getAge()).as("check %s's age", frodo.getName()).isEqualTo(33);
} catch (AssertionError e) {
  assertThat(e).hasMessage("[check Frodo's age] expected:<[33]> but was:<[50]>");
}

Where that quoted string in the catch block hasMessage is what appears in your unit test output log if the assertion fails.


I found this by noticing the failWithMessage helper in the custom assert page linked in the question. The JavaDoc for that method points out that it is protected, so it can't be used by callers to set a custom message. It does however mention the as helper:

Moreover, this method honors any description set with as(String, Object...) or overridden error message defined by the user with overridingErrorMessage(String, Object...).

... and the overridingErrorMessage helper, which completely replaces the standard AssertJ expected: ... but was:... message with the new string provided.

The AssertJ homepage doesn't mention either helper until the features highlights page, which shows examples of the as helper in the Soft Assertions section, but doesn't directly describe what it does.


To add another option to Patrick M's answer:

Instead of using Descriptable.as, you can also use AbstractAssert.withFailMessage():

try {
  // set a bad age to Mr Frodo which is really 33 years old.
  frodo.setAge(50);
  // you can specify a test description via withFailMessage(), supports String format args
  assertThat(frodo.getAge()).
    withFailMessage("Frodo's age is wrong: %s years, difference %s years",
      frodo.getAge(), frodo.getAge()-33).
    isEqualTo(33);
} catch (AssertionError e) {
  assertThat(e).hasMessage("Frodo's age is wrong: 50 years, difference 17 years");
}

The difference to using Descriptable.as is that it gives you complete control over the custom message - there is no "expected" and "but was".

This is useful where the actual values being tested are not useful for presentation - this method allows you to show other, possibly calculated values instead, or none at all.


Important Note that, just like Descriptable.as, you must call withFailMessage() before any actual assertions - otherwise it will not work, as the assertion will fire first. This is noted in the Javadoc.