Can you add a custom message to AssertJ assertThat?

We have a test suite that primarily uses JUnit assertions with Hamcrest matchers. One of our team started experimenting with AssertJ and impressed people with its syntax, flexibility and declarative-ness. There is one feature that JUnit provides that I can't find an equivalent for in AssertJ: adding a custom assert failure message.

We're often comparing objects that are not made for human readability and will have random-seeming Ids or UUIDs and it's impossible to tell what they're supposed to be by the data they contain. This is an unavoidable situation for our codebase, sadly, as part of the purpose it fulfills is mapping data between other services without necessarily understanding what it is.

In JUnit, the assertThat method provides a version with a String reason parameter before the Matcher<T> param. This makes it trivial to add a short debug string shedding some light on the problem, like what the comparison should mean to a human.

AssertJ, on the other hand, provides a jillion different genericized static assertThat methods which return some form of interface Assert or one of its many implementing classes. This interface does not provide a standard way of setting a custom message to be included with failures.

Is there any way to get this functionality from the AssertJ API or one of its extensions without having to create a custom assert class for every assert type we want to add messages to?


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.


Do 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.