Search

Dark theme | Light theme

October 1, 2024

Awesome AssertJ: Using Our Own Assertions Class

AssertJ already provides many useful assertions for all kind of types. But sometimes we want to define our own assertions for our own types. We can define new assertions by extending the AbstractAssert class In this class we add methods that will check the values of our type. The names of the methods can reflect the domain model of our type. This can make our tests more readable and understandable.

The abstract class AbstractAssert has a constructor that takes two arguments. We must pass the object we want to test and the class of the assertion class. The object that is passed is assigned to the property actual of the class. It is good practice to add a static method assertThat to the class that will return a new instance of the assertion class. Next we can add our own methods to the class. Within these methods we write the code to assert the values of our type. If the assertion would fail we can use the failWithMessage method to provide a message to the user.

AbstractAssert has also useful methods like isNotNull and isNotEqualTo that we can use in our assertions.

In the following example we write a custom assertion class for the type Pirate. The class Pirate has a nested object of type Ship. We define these with the following two records:

// File: mrhaki/Ship.java
package mrhaki;

public record Ship(String name, String type, int crewSize) {}
// File: mrhaki/Pirate.java
package mrhaki;

public record Pirate(String name, String rank, Ship ship) {}

Next we create our custom assertion class PirateAssert. We extend the class AbstractAssert, add a static method assertThat and the two assertion methods hasName and belongsOnShipWithName:

// File: mrhaki/PirateAssert.java
package mrhaki;

import org.assertj.core.api.AbstractAssert;

import java.util.Objects;

/**
 * Custom assertion methods for a Pirate instance.
 */
public class PirateAssert extends AbstractAssert<PirateAssert, Pirate> {
    protected PirateAssert(Pirate pirate) {
        super(pirate, PirateAssert.class);
    }

    /**
     * Method to create a new instance of PirateAssert, using the
     * same naming as standard AssertJ methods.
     * @param pirate Pirate instance to assert.
     * @return New PirateAssert instance.
     */
    public static PirateAssert assertThat(Pirate pirate) {
        return new PirateAssert(pirate);
    }

    /**
     * Check if the given name is equal to the actual
     * pirate's name.
     * @param name Expected name of the pirate.
     * @return Current instance for fluent API.
     */
    public PirateAssert hasName(String name) {
        // First check the actual Pirate instance is not null.
        isNotNull();

        // The Pirate instance passed in the assertThat method
        // is assigned to a variable with the name actual.
        // For comparison, we need to use that name.
        if (!Objects.equals(actual.name(), name)) {
            // Create an assertion failure with a message
            // that will be shown when the name is not equal to the actual name.
            failWithMessage("Expected pirate's name to be <%s> but was <%s>", name, actual.name());
        }

        // For fluent API usage we return this.
        return this;
    }

    /**
     * Check if the given name is equal to the name of the ship
     * the pirate belongs to.
     * @param name Expected name of the ship.
     * @return Current instance for fluent API.
     */
    public PirateAssert belongsOnShipWithName(String name) {
        isNotNull();

        if (Objects.isNull(actual.ship())) {
            failWithMessage("Expected pirate to belong on ship with name <%s> but was not found on any ship", name);
        }

        if (!Objects.equals(actual.ship().name(), name)) {
            failWithMessage("Expected pirate to belong on ship with name <%s> but was <%s>", name, actual.ship().name());
        }

        return this;
    }
}

In the following test we use the custom assertion class PirateAssert to assert the values of a Pirate instance:

// File: mrhaki/CustomPirateAssertion.java
package mrhaki;

import org.junit.jupiter.api.Test;

import static mrhaki.PirateAssert.assertThat;

public class CustomPirateAssertion {

    @Test
    void checkPirate() {
        // given
        Ship ship = new Ship("Black Pearl", "Galleon", 100);
        Pirate pirate = new Pirate("Jack Sparrow", "Captain", ship);


        // expect
        assertThat(pirate).hasName("Jack Sparrow")
                          .belongsOnShipWithName("Black Pearl");
    }
}

Written with AssertJ 3.26.3.