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 Window  Preferences  Java  Editor  Content assist  Favorites  New Type, 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 an other 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