This tutorial describes the usage of the AssertJ framework for writing unit tests in Java.
1. Introduction to AssertJ
The AssertJ project provides fluent assertion statements for test code written in Java.
These assert statements are typically used with Java JUnit tests.
The base method for AssertJ assertions is the assertThat
method followed by the assertion.
AssertJ is a fork of the Fest assert library, as Fest is not actively maintained anymore.
Date today = new Date();
assertThat(birthday).isBefore(today);
AssertJ assert statements try to create readable errors messages for test errors.
List<String> list = new ArrayList<>();
assertTrue(list.contains("abc"));
->
java.lang.AssertionError at ...
assertThat(list).contains("abc");
->
java.lang.AssertionError:
Expecting:
<[]>
to contain:
<["abc"]>
but could not find:
<["abc"]>
2. Using AssertJ
As AssertJ is a Java library you can use Gradle or Maven to add it your project. Check https://mvnrepository.com/artifact/org.assertj/assertj-core to get the latest version.
2.1. Gradle
To use AssertJ in your Gradle build for unit tests add the following dependency to your Gradle build file.
testImplementation 'org.assertj:assertj-core:3.21.0'
2.2. Maven
To use the library for a Maven based project, the following dependency to your pom file.
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.21.0</version>
<scope>test</scope>
</dependency>
3. IDE configuration
3.1. Eclipse IDE configuration
To simplify the usage of the AssertJ assertThat
statement in the Eclipse IDE go to , enter org.assertj.core.api.Assertions
and confirm.
You should see org.assertj.core.api.Assertions.*
in the list.
3.2. IntelliJ Idea configuration
No special is needed configuration, just start typing assertThat
and then invoke completion (Ctrl-Space) twice.
4. Usage of AssertJ
4.1. Writing assertions
AssertJ assertions start with a call to assertThat followed by an assertion.
assertThat("abc").isEqualTo(123);
4.2. Chaining of assertions
AssertJ allows you to be concise by chaining multiple assertions.
// AssertJ
assertThat(list).isNotNull().isNotEmpty();
4.3. Assertions for Java collections
AssertJ comes with many built in matchers for Java collection classes.
For example the List class:
assertThat(userList).contains(user, atIndex(0)).containsOnlyOnce(user, user2, user3);
assertThat(userList).contains(user).isSortedAccordingTo(ageComparator);
Here is an example that tests a Map:
// check for multiple keys at once
assertThat(map).containsKeys("a", "b", "c");
// check if the value of a certain key satisfies a condition
assertThat(map).hasEntrySatisfying(key, String::isEmpty);
// check if all entries of another map are contained in a map
assertThat(map).containsAllEntriesOf(expectedSubset);
4.4. Assertions for Date
AssertJ provides special assertions for the Java date class.
assertThat(eclipseOxygen.getReleaseDate()).isBeforeYear(2020).isAfterYear(2016);
assertThat(eclipseOxygen.getReleaseDate()).isBetween("2017-01-31", "2017-12-31");
4.5. Assertions for File and Stream
There are assertions to handle common file and stream checks.
// file assertions:
assertThat(manifestFile).exists();
assertThat(contentOf(manifestFile)).startsWith("Manifest-Version:");
// stream assertions:
assertThat(streamFromFile).hasSameContentAs(streamFrom(streamFromFileCopy));
4.6. Adding a custom message to an assertion
We can add a customized message to existing assertions to better explain what we are expecting.
For this purpose we use the as()
method in our assertion chain:
User user = new User("admin");
assertThat(user.getPostCount()).as("User \"%s\" has no posts", user.getName()).isEqualTo(0);
4.7. Comparing objects field by field
Sometimes you don’t want to use the existing equals() method but just want to compare by certain fields. AssertJ provides multiple assertions to help you with that:
// new
assertThat(user).usingRecursiveComparison().isEqualTo(user);
assertThat(user).usingRecursiveComparison().ignoringFields("address", "age").isEqualTo(expected)
4.8. Further examples
The following example code is taken from the AssertJ homepage:
// unique entry point to get access to all assertThat methods and utility methods (e.g. entry)
import static org.assertj.core.api.Assertions.*;
// common assertions
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron)
.isIn(fellowshipOfTheRing);
// String specific assertions
assertThat(frodo.getName()).startsWith("Fro")
.endsWith("do")
.isEqualToIgnoringCase("frodo");
// collection specific assertions
assertThat(fellowshipOfTheRing).hasSize(9)
.contains(frodo, sam)
.doesNotContain(sauron);
// using extracting magical feature to check fellowshipOfTheRing characters name :)
assertThat(fellowshipOfTheRing)
.extracting("name")
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");
// map specific assertions, ringBearers initialized with the elves rings and the one ring bearers.
assertThat(ringBearers).hasSize(4)
.contains(entry(oneRing, frodo), entry(nenya, galadriel))
.doesNotContainEntry(oneRing, aragorn);
// and many more assertions : dates, file, numbers, exceptions ...
5. Using AssertJ matchers in unit tests
The target of this exercise is to make yourself familiar with AssertJ matchers. The tests are trivial but you will get familiar with the usage of the AssertJ matchers.
5.1. Implement the test
Create the following new test class called AssertJExamplesTests
.
package com.vogella.unittest.assertj;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
public class AssertJExamplesTests {
// tests for the list
@DisplayName("Tests for the List")
@Nested
class ListTests {
private List<Integer> list;
@BeforeEach
public void setup() {
list = Arrays.asList(5, 2, 4);
}
@Test
@DisplayName("List should have an intial size of 3")
void ensureInitialSize() {
fail();
}
@Test
@DisplayName("Check content of the array")
void containsNumbersInAnyOrder() {
fail();
} private Integer[] ints;
@Test
void everyItemGreaterThan1() {
fail();
}
}
@DisplayName("Tests for the array")
@Nested
class ArrayTests {
private Integer[] ints;
@BeforeEach
public void setup() {
ints = new Integer[] { 7, 5, 12, 16 };
}
@Test
void arrayHasSizeOf4() {
fail();
}
@Test
void arrayContainsNumbersInGivenOrder() {
fail();
}
}
@DisplayName("Tests for the Task")
@Nested
class TaskTests {
// class to be tested
public class Task {
private final long id;
private String summary;
private String description;
private int year;
public Task(long id, String summary, String description) {
this.id = id;
this.summary = summary;
this.description = description;
}
// getters/setters
}
// tests for the Task
@Test
void objectHasSummaryProperty() {
Task task = new Task(1, "Learn Hamcrest", "Important");
fail();
}
@Test
void objectHasCorrectSummaryValue() {
Task task = new Task(1, "Learn Hamcrest", "Important");
fail();
}
@Test
void objectHasSameProperties() {
Task todo1 = new Task(1, "Learn Hamcrest", "Important");
Task todo2 = new Task(1, "Learn Hamcrest", "Important");
fail();
}
}
@DisplayName("Tests for String")
@Nested
class StringTests {
// tests for string
@Test
void ensureThatAnEmptryStringIsEmpty() {
String input = "";
fail();
}
@Test
void ensureThatAStringIsEitherNullOrEmpty() {
String input = "";
fail();
}
}
}
6. Exercise - Testing a data model using AssertJ matchers
6.1. Implement the tests
Create the following test class.
package com.vogella.unittest.assertj;
import static com.vogella.unittest.model.Race.HOBBIT;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
import com.vogella.unittest.model.TolkienCharacter;
import com.vogella.unittest.services.DataService;
class TolkienChecksWithAssertJTestss {
@Test
void generalMatchersForLists( ) {
List<Integer> list = Arrays.asList(5, 2, 4);
// test has list has size of 3
// contains the elements 2, 4, 5 in any order
// That every item is greater than 1
fail("not implemented yet");
}
@Test
void tolkienCharacterShouldHaveProperties() {
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
// ensure that TolkienCharacter has the bean property "name"
// ensure that TolkienCharacter has the bean property "age" with is greater than 0
// Hint: maybe you need to change your data model
fail("not implemented yet");
}
@Test
void validateTolkeinCharactorsInitializationWorks() {
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
//age is 33
//name is "Frodo"
//name is not "Frodon"
fail("not implemented yet");
}
@Test
void ensureThatFellowsAreNotOrcs() {
List<TolkienCharacter> fellowship = new DataService().getFellowship();
// ensure that no fellows is a orc
fail("not implemented yet");
}
@Test
void ensureFrodoIsAHobbitWithCondition() {
TolkienCharacter frodo = new DataService().getFellowshipCharacter("Frodo");
fail("Use aHobbit from below in an assert statement is()");
}
HobbitCheck aHobbit = new HobbitCheck();
private final class HobbitCheck extends Condition<TolkienCharacter> {
@Override
public boolean matches(TolkienCharacter value) {
return value.getRace() == Race.HOBBIT;
}
}
}
Fix all the failing test. Try to write reasonable tests which fit the method name.
6.2. Verify
Run your new test via the IDE.
7. Exercise: Creating a custom assertion for a class
While it is nice to have the ability to define custom error messages in the test, if we want to use this in multiple tests this can be come tedious and leads to redundant code. In this case it is useful to created custom assertion classes that are reusable.
Create for this exercise the following example class which we want to test:
package com.vogella.unittest.assertj;
public class User {
int posts;
String name;
public int getPostCount() {
return 0;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(String name) {
this.name = name;
}
}
We could write a test with regular AssertJ.
@Test
public void newlyCreatedUserHasNoPosts() throws Exception {
User user = new User("admin");
assertThat(user.getPostCount()).as("User \"%s\" has no posts", user.getName()).isEqualTo(0);
}
But for the sake of the exercise, we want to write our own custom assert statement which would allows us to write: And transform it into this:
@Test
public void newlyCreatedUserHasNoPosts() throws Exception {
User user = new User("admin");
assertThat(user).hasNoPosts();
}
7.1. Implementation
We create the missing assertion class.
public class UserAssert extends AbstractAssert<UserAssert, User> {
public UserAssert hasNoPosts() {
// check that actual User we want to make assertions on is not null.
isNotNull();
// overrides the default error message with a more explicit one
String assertjErrorMessage = "\nExpecting User <%s> to have no posts\n but was:\n <%s> posts";
// null safe check
int actualNumberOfPosts = actual.getPostCount();
if (!Objects.areEqual(actualNumberOfPosts, 0)) {
failWithMessage(assertjErrorMessage, actual.getName(), actualNumberOfPosts);
}
// return the current assertion for method chaining
return this;
}
public UserAssert(User actual) {
super(actual, UserAssert.class);
}
public static UserAssert assertThat(User actual) {
return new UserAssert(actual);
}
}
We have to define our entry point class that lets us start the assertion chain.
For this we provide a static assertThat
method that returns our assertion class.
public class Assertions {
public static UserAssert assertThat(User actual) {
return new UserAssert(actual);
}
}
7.2. Validate
Now we can execute our test:
package com.vogella.unittest.assertj;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
class AssertWithCustomAssertTest {
@Test
void ensureNewlyCreatedUserHasNoPosts() {
User user = new User("admin");
assertThat(user.getPostCount()).as("User \"%s\" has no posts", user.getName()).isEqualTo(0);
}
@Test
public void newlyCreatedUserHasNoPosts() throws Exception {
User user = new User("admin");
com.vogella.unittest.assertj.Assertions.assertThat(user).hasNoPosts();
}
}
for the custom annotation we use our assertThat statement. |
java.lang.AssertionError:
Expecting User <admin> to have no posts
but was:
<42> posts
Creating custom assertThat statements can be consuming therefore you can also generate these. See https://www.baeldung.com/assertj-custom-assertion for an exmaple. |
8. Using AssertJ Android
AssertJ Android allows you to write efficient assertions for Android. The following examples are taken from their Github page for AssertJ Android.
// check visibility of view with JUnit
assertEquals(View.GONE, view.getVisibility());
// AssertJ Androids version is much shorter
assertThat(view).isGone();
// AssertJ Android allows to combine checks
assertThat(layout).isVisible()
.isVertical()
.hasChildCount(4)
.hasShowDividers(SHOW_DIVIDERS_MIDDLE);
9. Converting JUnit assertions to AssertJ with a script
The AssertJ project provides scripts for all major operating systems to automatically convert tests with JUnit assertions to AssertJ.
You can download them from their Github repository: Script for Unix/Windows Script for OSX
Just copy the script into the folder of the tests you want to convert and execute it.
# Unix/Windows
convert-junit-assertions-to-assertj.sh
# OSX
convert-junit-assertions-to-assertj-on-osx.sh
1 - Replacing : assertEquals(0, myList.size()) ................. by : assertThat(myList).isEmpty()
2 - Replacing : assertEquals(expectedSize, myList.size()) ...... by : assertThat(myList).hasSize(expectedSize)
While this script works pretty well it can still introduce compile errors that you have to fix manually. For more information on this topic please see the AssertJ project website.
10. AssertJ resources
10.1. vogella Java example code
If you need more assistance we offer Online Training and Onsite training as well as consulting