Lesson 3: Advanced Assertions - AssertJ
1. Overview
In this lesson, we'll learn about AssertJ, another popular tool that facilitates assertions in the Java world.
We'll explore its fluent API and use it to test various data types and structures.
The relevant module we need to import when we're starting with this lesson is: advanced-assertions-assertj-start.
If we want to have a look at the fully implemented lesson as a reference, feel free to import: advanced-assertions-assertj-end.
2. AssertJ
AssertJ is a popular library that enables us to write declarative assertions in Java. A key characteristic of AssertJ is its fluent API that allows us to describe the expected behavior by chaining assertion methods.
We'll start by adding the AssertJ Maven dependency to our project:
After that, we can create the com.baeldung.lju.AssertJTest class and re-write the tests using Assertj. We’ll start most of the assertions from the static factory method Assertions.assertThat(). Although Hamcrest and JUnit expose static methods with the same name, we need to ensure we import the one from the org.assertj.core.api package:
We can now use assertThat() to create an object that facilitates various assertions, specific to the given data type. An advantage of AssertJ is that we can use the IDE to see all the possible validations supported by the API. Let’s use IntelliJ to see the available assertions for a String object:
Let’s use the methods containsIgnoringCase(), startsWith(), endsWith() to recreate the test from the previous section:
Similarly to the previous example, AssertJ’s assertThat() is polymorphic. As a result, we can use it on a different data type and we’ll be provided a different set of supported assertions. For instance, we can use it on a Temporal object, and we get assertions such as isBefore() and isAfter():
Additionally, AssertJ offers great support for testing Map and Collection objects. For example, we can validate the elements of a Collection using containsExactly() or containsExactlyInAnyOrder():
Furthermore, we can perform more complex assertions such as checking the value from a certain index or declaring a condition that will tested against all the elements:
On the other hand, for Maps, we can perform assertions to check certain key-value pairs:
Moreover, even if we don’t know all the entries in the Map, we can still validate the presence or absence of certain keys or values:
3. Advanced Assertions in Action
Now that we've become familiar with AssertJ's API, let's write a more realistic test for the InMemoryTaskRepository class. For example, let's look at the findByNameContainingAndAssigneeId method that can be used to query the created tasks based on the task name and the assigneeId.
Firstly, we’ll create a com.baeldung.lju.persistence.repository.impl.InMemoryTaskRepositoryTest class and set up some test data by creating a Worker and assigning it three Tasks, one of them using a different naming pattern:
We can now call the method we want to test, and request all the “BACKEND” tasks assigned to our test Worker:
Now we can perform the assertions. For instance, we can check that:
- the repository fetched exactly two tasks,
- both tasks are assigned to the worker with an id equal to 100,
- none of the tasks contains “FRONTEND” in their name,
- the returned tasks are equal to task1 and task2, regardless of their order
As we can see, AssertJ’s fluent API enabled us to write a clear and elegant assertion that explicitly states our expectations for the function’s result.