AssertJ
AssertJ is the open-source library for assert statements that are helpful in test classes. The basic module you need to import is Core.
Besides the Core module there are special Guava, Joda Time, Neo4J, DB and Swing modules that require separate pom dependencies and imports.
Maven dependency and import statement
This is the Maven dependency to add for AssertJ Core:
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
It is included in spring-boot-starter-test as a transitive dependency so often you can take the import for granted. If you do it without Spring, a version number is required.
As import the following line is often sufficient:
import static org.assertj.core.api.Assertions.assertThat;
Documentation
There is very good documentation on https://assertj.github.io/doc/. There are also javadocs on javadoc.io.
In the javadocs, package org.assertj.core.api is the important package to study. In this package class AbstractAssert is the first one you might want to see.
This AbstractAssert class contains all methods that can be used on object types, like has(), is(), isEqualTo(), while its subclasses contain methods appropriate for more specific object types. Examples of subclasses are AbstractIterableAssert, AbstractDateAssert and AtomicLongAssert.
The other class, actually the one of which you import the .assertThat() method, is Assertions. It has about as many overloads for the .assertThat() method as there are data- and objecttypes. The .assertThat() method is a static factory method that returns objects like ListAssert<T>
. Such -Assert objects are actual children in the tree that starts with AbstractAssert.
Typical use
The typical simple way to use AssertJ core is like this:
assertThat(townList).hasSize(10);
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron);
It is never sufficient to just use the static assertThat() method without adding at least one chain element. Remember that the .assertThat() method is a static factory method that does not test anything and will not throw any exception.
Instead of using one test method, you can create longer chains (I copied examples both from the documentation and the Baeldung site, they both use Frodo in their examples):
assertThat(frodo.getName())
.startsWith("Fro")
.endsWith("do")
.isEqualToIgnoringCase("frodo");
Top 10 methods
The number of test methods is enormous. The makers probably coded whatever they could think of. Trying to remember them all is kind of useless, so I asked ChatGPT to list a top 10. Here is the answer:
- isEqualTo()
- isNotEqualTo()
- isNull() / isNotNull()
- isTrue() / isFalse()
- hasSize() (collections, arrays, maps, strings)
- contains() / doesNotContain()
- containsExactly() / containsExactlyInAnyOrder()
- startsWith() / endsWith() / contains() ( for Strings)
- throwable assertions
- extracting() / filterOn()
The latter two need some explanation:
Throwable assertions
ChatGPT gives the following example:
assertThatThrownBy(() -> service.save(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("must not be null");
From the javadocs I picked this one:
assertThatThrownBy(() -> { throw new Exception("boom!"); })
.isInstanceOf(Exception.class)
.hasMessageContaining("boom");
As argument you need a functional interface of type ThrowableAssert.ThrowingCallable. It has no arguments or return type and throws a Throwable. With this method you check whether your methods throw the expected exceptions.
Note that it requires an extra import, unless you already imported all methods from the Assertions class with a wildcard.
extracting() and filterOn()
These methods are to be applied on collections of objects. ChatGPT provided these examples:
assertThat(customers)
.extracting(Customer::getName)
.contains("Alice", "Bob");
assertThat(customers)
.filteredOn(c -> c.getAge() > 18)
.extracting(Customer::getName)
.contains("Alice");
These methods are used to check if collections of objects contain objects with specific values set for specific fields.
Customizing the error message
Tests must result in readable output that tell you which tests failed and how they failed. There are two ways in which you can customize the terminal output on a failed test. The first is probably the one you need.
Using ‘as()’ for assertion description
One of the methods you can chain is .as(). It comes from the Descriptable interface that is implemented in all Assert classes. There are three overrides, you can simple supply a String, a String with a varargs argument to fill the bind variables or a Supplier functional interface with a String type return value.
The first example in the documentation is this one:
assertThat(frodo.getAge()).as("check %s's age", frodo.getName())
.isEqualTo(100);
output:
[check Frodo's age] expected:<100> but was:<33>
It is the pattern similar to System.out.printf(String str, ... args)
. The error message itself is unchanged, but the text preceeding it is added which makes things better readable.
It is important that you insert the ‘.as()’ method before the actual test method in the chain. Otherwise he will be ignored.
withFailMessage() and overridingErrorMessage()
Besides ‘as()’ there are also the .withFailMessage() and .overridingErrorMessage(). The two methods have different names but do exactly the same. You can use either a variant with two parameters (String and varargs) or a single parameter (Supplier, returning String type). Instead of adding some text before the error message, they change the error message itself.
Example from javadoc:
assertThat(player.isRookie())
.overridingErrorMessage("Expecting Player <%s> to be a rookie but was not.", player)
.isTrue();
// or with supplier:
assertThat(player.isRookie())
.overridingErrorMessage(() -> "Expecting Player to be a rookie but was not.")
.isTrue();
Be aware that the .isTrue() method is crucial, without it no testing is done at all.
Controlling type formatting with Representation
More extensive description of this topic can be found here.
The way AssertJ formats all different types for output can be adjusted. To do so you need to customize the Representation type object that is being used. Representation is an AssertJ interface and the relevant implementing class is StandardRepresentation.
What you can do is create your own Representation class by extending StandardRepresentation. You can override methods then that define the formatting.
Your custom Representation class must be registered, before the test methods are called. Therefore you use this line:
Assertions.useRepresentation(new MyCustomRepresentation());
‘Consider writing a JUnit 5 extension implementing BeforeAllCallback to make sure the representation is set once for all before any test is executed.’
If the custom representation should only be used by a single method, you can do this:
Representation customRepresentation = new CustomRepresentation();
assertThat(new Example()).withRepresentation(customRepresentation)
.isNull();
This is it for now.