Testing complex objects often requires comparing all fields deeply — including nested objects. If the class does not override equals(), a simple isEqualTo() will only compare references. AssertJ's usingRecursiveComparison() performs a deep field-by-field comparison without requiring equals() to be defined.
Why This Matters
In many projects, domain objects (especially DTOs or entities) don't define equals(). Rather than polluting the domain model with test-only equality logic, usingRecursiveComparison() keeps that concern in the test layer.
Ignoring Fields
You can exclude specific fields from comparison — for example, auto-generated IDs or timestamps — using ignoringFields("id", "createdAt") or ignoringFieldsMatchingRegexes(".*Id").
Example
package com.logicbig.example;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
public class RecursiveComparisonExample {
static class Address {
String city;
String zip;
Address(String city,
String zip) {
this.city = city;
this.zip = zip;
}
}
static class Customer {
int id;
String name;
Address address;
Customer(int id,
String name,
Address address) {
this.id = id;
this.name = name;
this.address = address;
}
}
@Test
void myTest() {
Customer actual = new Customer(1,
"Alice",
new Address("London", "SW1A"));
Customer expected = new Customer(99,
"Alice",
new Address("London", "SW1A"));
// ignore id (differs), compare everything else deeply
assertThat(actual).usingRecursiveComparison()
.ignoringFields("id")
.isEqualTo(expected);
System.out.println("Recursive comparison with ignored field passed");
// full comparison - same id
Customer same = new Customer(1, "Alice", new Address("London", "SW1A"));
assertThat(actual).usingRecursiveComparison().isEqualTo(same);
System.out.println("Full recursive comparison passed");
}
}
Output$ mvn clean test -Dtest=* [INFO] Scanning for projects... [INFO] [INFO] ---------< com.logicbig.example:assertj-recursive-comparison >---------- [INFO] Building assertj-recursive-comparison 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ assertj-recursive-comparison --- [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ assertj-recursive-comparison --- [INFO] skip non existing resourceDirectory D:\example-projects\assertj\assertj-recursive-comparison\src\main\resources [INFO] [INFO] --- compiler:3.11.0:compile (default-compile) @ assertj-recursive-comparison --- [INFO] No sources to compile [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ assertj-recursive-comparison --- [INFO] skip non existing resourceDirectory D:\example-projects\assertj\assertj-recursive-comparison\src\test\resources [INFO] [INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ assertj-recursive-comparison --- [INFO] Changes detected - recompiling the module! :source [INFO] Compiling 1 source file with javac [debug target 17] to target\test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ assertj-recursive-comparison --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.RecursiveComparisonExample Recursive comparison with ignored field passed Full recursive comparison passed [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.173 s -- in com.logicbig.example.RecursiveComparisonExample [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.852 s [INFO] Finished at: 2026-03-03T14:22:38+08:00 [INFO] ------------------------------------------------------------------------
Conclusion
The output confirms that recursive comparison correctly evaluates field equality deeply, including nested Address fields. The ignored field id is excluded from the comparison, simulating the common scenario where generated identifiers differ between the actual result and the expected template.
Example ProjectDependencies and Technologies Used: - assertj-core 3.27.7 (Rich and fluent assertions for testing in Java)
- junit-jupiter-engine 6.0.2 (Module "junit-jupiter-engine" of JUnit)
- JDK 17
- Maven 3.9.11
|
|