Awesome AssertJ: Comparing Objects Recursively
To compare nested objects we can use the usingRecursiveComparison()
method in AssertJ. We can set up the nested objects with values we expect, invoke a method that would return the actual nested objects, and then use the usingRecursiveComparison()
method to compare the actual nested objects with the expected nested objects. This is a very clean way to compare nested objects. Also when we would add a new property to the nested objects our test would fail as we didn’t use that new property yet for our expected nested objects.
In the following example test we use the usingRecursiveComparison()
method to compare actual nested objects with the expected nested objects. Our nested objects are of type Pirate
and Ship
.
// 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) {}
This is our class we want to test. The class PirateShipCreator
creates the nested objects we want to write assertions for.
// File: mrhaki/PirateShipCreator.java
package mrhaki;
public class PirateShipCreator {
public static Pirate createJackSparrow() {
return new Pirate("Jack Sparrow", "Captain", createBlackPearl());
}
public static Pirate createDavyJones() {
return new Pirate("Davy Jones", "Captain", createFlyingDutchman());
}
private static Ship createBlackPearl() {
return new Ship("Black Pearl", "Galleon", 100);
}
private static Ship createFlyingDutchman() {
return new Ship("Flying Dutchman", "Ghost ship", 199);
}
}
The test class PirateShipCreatorTest
uses the usingRecursiveComparison()
method to compare actual nested objects with the expected nested objects:
// File: mrhaki/PirateShipCreatorTest.java
package mrhaki;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class PirateShipCreatorTest {
@Test
public void testPirateEquality() {
// given
Ship expectedShip = new Ship("Black Pearl", "Galleon", 100);
Pirate expectedPirate = new Pirate("Jack Sparrow", "Captain", expectedShip);
// when
Pirate actualPirate = PirateShipCreator.createJackSparrow();
// then
// assert equality using recursive comparison
assertThat(actualPirate)
.usingRecursiveComparison()
.isEqualTo(expectedPirate);
}
}
If we want to ignore a property we can use the ignoringFields(String)
method. Or if we want to ignore properties of a certain type we can use the ignoringFieldsOfTypes(Class<?>)
method. This can be very useful for properties that store dates we cannot setup properly in our tests.
Instead of ignoring fields we can also specify which fields we want to compare with the comparingOnlyFields(String)
method. And there is a comparingOnlyFieldsOfTypes(Class<?>)
method to specify which fields of a certain type we want to compare.
In the following tests we use all four methods to ignore or include fields in our comparison.
// File: mrhaki/PirateShipCreatorTest.java
package mrhaki;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class PirateShipCreatorTest {
@Test
public void testPirateEqualityIgnoringShipCrewSize() {
// given
Ship expectedShip = new Ship("Flying Dutchman", "Ghost Ship", 100);
Pirate expectedPirate = new Pirate("Davy Jones", "Captain", expectedShip);
// when
Pirate actualPirate = PirateShipCreator.createDavyJones();
// then
// assert equality using recursive comparison, ignoring crew size
assertThat(actualPirate)
.usingRecursiveComparison()
.ignoringFields("ship.crewSize")
.isEqualTo(expectedPirate);
}
@Test
public void testPirateEqualityIgnoringIntegerFields() {
// given
Ship expectedShip = new Ship("Flying Dutchman", "Ghost Ship", 100);
Pirate expectedPirate = new Pirate("Davy Jones", "Captain", expectedShip);
// when
Pirate actualPirate = PirateShipCreator.createDavyJones();
// then
// assert equality using recursive comparison, ignoring integer fields
assertThat(actualPirate)
.usingRecursiveComparison()
.ignoringFieldsOfType(Integer.class)
.isEqualTo(expectedPirate);
}
@Test
public void testPirateEqualityComparingSelectedFields() {
// given
Ship expectedShip = new Ship("Flying Dutchman", "Ghost Ship", 100);
Pirate expectedPirate = new Pirate("Davy Jones", "Captain", expectedShip);
// when
Pirate actualPirate = PirateShipCreator.createDavyJones();
// then
// assert equality using recursive comparison, comparing only selected fields
assertThat(actualPirate)
.usingRecursiveComparison()
.comparingOnlyFields("name", "rank", "ship.name", "ship.type")
.isEqualTo(expectedPirate);
}
@Test
public void testPirateEqualityComparingSelectedTypeOfFields() {
// given
Ship expectedShip = new Ship("Flying Dutchman", "Ghost Ship", 100);
Pirate expectedPirate = new Pirate("Davy Jones", "Captain", expectedShip);
// when
Pirate actualPirate = PirateShipCreator.createDavyJones();
// then
// assert equality using recursive comparison,
// comparing only fields of type String and Ship
assertThat(actualPirate)
.usingRecursiveComparison()
.comparingOnlyFieldsOfTypes(String.class, Ship.class)
.isEqualTo(expectedPirate);
}
}
Written with AssertJ 3.26.3.