public class RecursiveComparisonAssert<SELF extends RecursiveComparisonAssert<SELF>> extends AbstractAssert<SELF,Object>
actual, info, myself, objects, throwUnsupportedExceptionOnEquals| Constructor and Description |
|---|
RecursiveComparisonAssert(Object actual,
RecursiveComparisonConfiguration recursiveComparisonConfiguration) |
| Modifier and Type | Method and Description |
|---|---|
SELF |
comparingOnlyFields(String... fieldNamesToCompare)
Makes the recursive comparison to only compare given actual fields and their subfields (no other fields will be compared).
|
RecursiveComparisonConfiguration |
getRecursiveComparisonConfiguration()
Returns the
RecursiveComparisonConfiguration currently used. |
SELF |
ignoringActualEmptyOptionalFields()
Makes the recursive comparison to ignore all actual empty optional fields (including
Optional, OptionalInt, OptionalLong and OptionalDouble),
note that the expected object empty optional fields are not ignored, this only applies to actual's fields. |
SELF |
ignoringActualNullFields()
Makes the recursive comparison to ignore all actual null fields (but note that the expected object null fields are used in the comparison).
|
SELF |
ignoringAllOverriddenEquals()
This method instructs the recursive comparison to compare recursively all fields including the one whose type have overridden equals,
except fields with java types (at some point we need to compare something!).
|
SELF |
ignoringCollectionOrder()
Makes the recursive comparison to ignore collection order in all fields in the object under test.
|
SELF |
ignoringCollectionOrderInFields(String... fieldsToIgnoreCollectionOrder)
Makes the recursive comparison to ignore collection order in the object under test specified fields.
|
SELF |
ignoringCollectionOrderInFieldsMatchingRegexes(String... regexes)
Makes the recursive comparison to ignore collection order in the object under test fields matching the specified regexes.
|
SELF |
ignoringExpectedNullFields()
Makes the recursive comparison to ignore all expected null fields.
|
SELF |
ignoringFields(String... fieldNamesToIgnore)
Makes the recursive comparison to ignore the given object under test fields.
|
SELF |
ignoringFieldsMatchingRegexes(String... regexes)
Makes the recursive comparison to ignore the object under test fields whose name matche the given regexes.
|
RecursiveComparisonAssert<?> |
ignoringFieldsOfTypes(Class<?>... typesToIgnore)
Makes the recursive comparison to ignore the object under test fields of the given types.
|
SELF |
ignoringOverriddenEqualsForFields(String... fields)
In case you have instructed the recursive to use overridden
equals with usingOverriddenEquals(),
this method allows to ignore overridden equals for the given fields (it adds them to the already registered ones). |
SELF |
ignoringOverriddenEqualsForFieldsMatchingRegexes(String... regexes)
In case you have instructed the recursive to use overridden
equals with usingOverriddenEquals(),
this method allows to force a recursive comparison for the fields matching the given regexes (it adds them to the already registered ones). |
SELF |
ignoringOverriddenEqualsForTypes(Class<?>... types)
By default the recursive comparison uses overridden
equals methods to compare fields,
this method allows to force a recursive comparison for all fields of the given types (it adds them to the already registered ones). |
SELF |
isEqualTo(Object expected)
Asserts that the object under test (actual) is equal to the given object when compared field by field recursively (including
inherited fields are included in the comparison).
|
SELF |
isNotEqualTo(Object other)
Asserts that actual object is not equal to the given object based on a recursive property/field by property/field comparison
(including inherited ones).
|
SELF |
usingOverriddenEquals()
By default the recursive comparison compare recursively all fields including the ones whose type have overridden equals
except fields with java types (at some point we need to compare something!).
|
SELF |
withComparatorForFields(Comparator<?> comparator,
String... fieldLocations)
Allows to register a comparator to compare fields with the given locations.
|
<T> SELF |
withComparatorForType(Comparator<? super T> comparator,
Class<T> type)
Allows to register a comparator to compare the fields with the given type.
|
SELF |
withEqualsForFields(BiPredicate<?,?> equals,
String... fieldLocations)
Allows to register a
BiPredicate to compare fields with the given locations. |
<T> SELF |
withEqualsForType(BiPredicate<? super T,? super T> equals,
Class<T> type)
Allows to register a
BiPredicate to compare the fields with the given type. |
SELF |
withErrorMessageForFields(String message,
String... fieldLocations)
Overrides an error message which would be shown when differences in the given fields while comparison occurred
with the giving error message.
|
SELF |
withErrorMessageForType(String message,
Class<?> type)
Overrides an error message which would be shown when differences for the giving type while comparison occurred with
the giving error message.
|
SELF |
withStrictTypeChecking()
Makes the recursive comparison to check that actual's type is compatible with expected's type (and do the same for each field).
|
areEqual, asInstanceOf, asList, assertionError, asString, describedAs, descriptionText, doesNotHave, doesNotHaveSameClassAs, doesNotHaveSameHashCodeAs, doesNotHaveToString, equals, extracting, extracting, failure, failureWithActualExpected, failWithActualExpectedAndMessage, failWithMessage, getWritableAssertionInfo, has, hashCode, hasSameClassAs, hasSameHashCodeAs, hasToString, inBinary, inHexadecimal, is, isElementOfCustomAssert, isExactlyInstanceOf, isIn, isIn, isInstanceOf, isInstanceOfAny, isInstanceOfSatisfying, isNot, isNotExactlyInstanceOf, isNotIn, isNotIn, isNotInstanceOf, isNotInstanceOfAny, isNotNull, isNotOfAnyClassIn, isNotSameAs, isNull, isOfAnyClassIn, isSameAs, matches, matches, newListAssertInstance, overridingErrorMessage, overridingErrorMessage, satisfies, satisfies, satisfies, satisfiesAnyOf, satisfiesAnyOf, satisfiesAnyOfForProxy, satisfiesForProxy, setCustomRepresentation, setDescriptionConsumer, setPrintAssertionsDescription, throwAssertionError, usingComparator, usingComparator, usingDefaultComparator, usingRecursiveComparison, usingRecursiveComparison, withFailMessage, withFailMessage, withRepresentation, withThreadDumpOnErrorclone, finalize, getClass, notify, notifyAll, toString, wait, wait, waitas, as, as, describedAspublic RecursiveComparisonAssert(Object actual, RecursiveComparisonConfiguration recursiveComparisonConfiguration)
public SELF isEqualTo(Object expected)
RecursiveComparisonConfiguration was used to help users understand the failure.
This is typically useful when actual's equals was not overridden.
The comparison is not symmetrical since it is limited to actual's fields, the algorithm gather all actual's fields
and then compare them to the corresponding expected's fields.
It is then possible for the expected object to have more fields than actual which is handy when comparing a base type to a subtype.
Strict/lenient recursive comparison
By default the objects to compare can be of different types but must have the same properties/fields. For example if object under test has a work field of type Address,
the expected object to compare the object under test to must also have one but it can of a different type like AddressDto.
It is possible to enforce strict type checking by calling withStrictTypeChecking() and make the comparison fail whenever the compared objects or their fields are not compatible.
Compatible means that the expected object/field types are the same or a subtype of actual/field types, for example if actual is an Animal and expected a Dog, they will be compared field by field in strict type checking mode.
Ignoring null fields in the recursive comparison
When an object is partially populated, it can still be interesting to see if its populated values are correct against a fully populated object.
This possible by calling ignoringActualNullFields() before isEqualTo
but bear in mind that only actual null fields are ignored, said otherwise the expected object null fields are used in the comparison.
Recursive comparison use of overridden equals methods
By default the recursive comparison is not applied on fields whose classes have overridden the equals method,
concretely it means equals is used to compare these fields instead of keeping on applying the recursive comparison.
The rationale is that if a class has redefined equals then it should be used to compare instances unless having a good reason.
It is possible though to change this behavior and force recursive comparison by calling any of these methods (but before calling isEqualTo otherwise this has no effect!):
ignoringOverriddenEqualsForTypes(Class...) Any fields of these classes are compared recursivelyignoringOverriddenEqualsForFields(String...) Any given fields are compared recursivelyignoringOverriddenEqualsForFieldsMatchingRegexes(String...) Any fields matching one of these regexes are compared recursivelyignoringAllOverriddenEquals() except for java types, all fields are compared field by field recursively.The recursive comparison handles cycles.
Comparator used in the recursive comparison
By default floats are compared with a precision of 1.0E-6 and doubles with 1.0E-15.
You can specify a custom comparator or equals BiPredicate per (nested) fields or type with the methods below (but before calling isEqualTo otherwise this has no effect!):
withEqualsForType(BiPredicate, Class) for a given typewithEqualsForFields(BiPredicate, String...) for one or multiple fieldswithComparatorForType(Comparator, Class) for a given typewithComparatorForFields(Comparator, String...) for one or multiple fieldsNote that field comparators always take precedence over type comparators.
Example
Here is a basic example with a default RecursiveComparisonConfiguration, you can find other examples for each of the method changing the recursive comparison behavior
like ignoringFields(String...).
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
Date ownedSince;
}
public static class Address {
int number;
String street;
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.ownedSince = new Date(123);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person sherlock2 = new Person("Sherlock", 1.80);
sherlock2.home.ownedSince = new Date(123);
sherlock2.home.address.street = "Baker Street";
sherlock2.home.address.number = 221;
// assertion succeeds as the data of both objects are the same.
assertThat(sherlock).usingRecursiveComparison()
.isEqualTo(sherlock2);isEqualTo in interface Assert<SELF extends RecursiveComparisonAssert<SELF>,Object>isEqualTo in class AbstractAssert<SELF extends RecursiveComparisonAssert<SELF>,Object>expected - the object to compare actual to.this assertion object.AssertionError - if the actual object is null.AssertionError - if the actual and the given objects are not deeply equal property/field by property/field.IntrospectionError - if one property/field to compare can not be found.public SELF isNotEqualTo(Object other)
This is typically useful when actual's equals was not overridden.
The comparison is not symmetrical since it is limited to actual's fields, the algorithm gather all
actual's fields and then compare them to the corresponding expected's fields.
It is then possible for the expected object to have more fields than actual which is handy when comparing
a base type to a subtype.
This method is based on isEqualTo(Object), you can check out more usages in that method.
Example
// equals not overridden in TolkienCharacter
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
TolkienCharacter frodoClone = new TolkienCharacter("Frodo", 33, HOBBIT);
TolkienCharacter youngFrodo = new TolkienCharacter("Frodo", 22, HOBBIT);
// Pass as equals compares object references
assertThat(frodo).isNotEqualTo(frodoClone);
// Fail as frodo and frodoClone are equals when doing a field by field comparison.
assertThat(frodo).usingRecursiveComparison()
.isNotEqualTo(frodoClone);
// Pass as one the age fields differ between frodo and youngFrodo.
assertThat(frodo).usingRecursiveComparison()
.isNotEqualTo(youngFrodo);isNotEqualTo in interface Assert<SELF extends RecursiveComparisonAssert<SELF>,Object>isNotEqualTo in class AbstractAssert<SELF extends RecursiveComparisonAssert<SELF>,Object>other - the object to compare actual to.this assertions objectAssertionError - if the actual object and the given objects are both null.AssertionError - if the actual and the given objects are equals property/field by property/field recursively.isEqualTo(Object)public SELF comparingOnlyFields(String... fieldNamesToCompare)
Specifying a field will make all its subfields to be compared, for example specifying person will lead to compare
person.name, person.address and all other Person fields.
On the other hand if you specify person.name, person won't be compared but person.name will be.
The fields are specified by name, not by value, for example you can specify person.name but not "Jack"
as "Jack" is not a field value.
comparingOnlyFields can be combined with ignoring fields methods to restrict further the fields actually compared,
the resulting compared fields = {specified compared fields} - {specified ignored fields}.
For example if the specified compared fields = {"foo", "bar", "baz"}
and the ignored fields = {"bar"} then only {"foo", "baz"} fields will be compared.
Usage example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person moriarty = new Person("Moriarty", 1.80);
moriarty.home.address.street = "Butcher Street";
moriarty.home.address.number = 221;
// assertion succeeds as name and home.address.street fields are not compared.
assertThat(sherlock).usingRecursiveComparison()
.comparingOnlyFields("height", "home.address.number")
.isEqualTo(moriarty);
// assertion fails as home.address.street fields differ.
assertThat(sherlock).usingRecursiveComparison()
.comparingOnlyFields("height", "home")
.isEqualTo(moriarty);fieldNamesToCompare - the fields of the object under test to compare in the comparison.RecursiveComparisonAssert to chain other methods.public SELF ignoringActualNullFields()
Example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
}
Person noName = new Person(null, 1.80);
noName.home.address.street = null;
noName.home.address.number = 221;
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
// assertion succeeds as name and home.address.street fields are ignored in the comparison
assertThat(noName).usingRecursiveComparison()
.ignoringActualNullFields()
.isEqualTo(sherlock);
// assertion fails as name and home.address.street fields are populated for sherlock but not for noName.
assertThat(sherlock).usingRecursiveComparison()
.ignoringActualNullFields()
.isEqualTo(noName);RecursiveComparisonAssert to chain other methods.public SELF ignoringActualEmptyOptionalFields()
Optional, OptionalInt, OptionalLong and OptionalDouble),
note that the expected object empty optional fields are not ignored, this only applies to actual's fields.
Example:
public class Person {
String name;
OptionalInt age;
OptionalLong id;
OptionalDouble height;
Home home = new Home();
}
public class Home {
String address;
Optional<String> phone;
}
Person homerWithoutDetails = new Person("Homer Simpson");
homerWithoutDetails.home.address.street = "Evergreen Terrace";
homerWithoutDetails.home.address.number = 742;
homerWithoutDetails.home.phone = Optional.empty();
homerWithoutDetails.age = OptionalInt.empty();
homerWithoutDetails.id = OptionalLong.empty();
homerWithoutDetails.height = OptionalDouble.empty();
Person homerWithDetails = new Person("Homer Simpson");
homerWithDetails.home.address.street = "Evergreen Terrace";
homerWithDetails.home.address.number = 742;
homerWithDetails.home.phone = Optional.of("(939) 555-0113");
homerWithDetails.age = OptionalInt.of(39);
homerWithDetails.id = OptionalLong.of(123456);
homerWithDetails.height = OptionalDouble.of(1.83);
// assertion succeeds as phone is ignored in the comparison
assertThat(homerWithoutDetails).usingRecursiveComparison()
.ignoringActualEmptyOptionalFields()
.isEqualTo(homerWithDetails);
// assertion fails as phone, age, id and height are not ignored and are populated for homerWithDetails but not for homerWithoutDetails.
assertThat(homerWithDetails).usingRecursiveComparison()
.ignoringActualEmptyOptionalFields()
.isEqualTo(homerWithoutDetails);RecursiveComparisonAssert to chain other methods.public SELF ignoringExpectedNullFields()
Example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person noName = new Person(null, 1.80);
noName.home.address.street = null;
noName.home.address.number = 221;
// assertion succeeds as name and home.address.street fields are ignored in the comparison
assertThat(sherlock).usingRecursiveComparison()
.ignoringExpectedNullFields()
.isEqualTo(noName);
// assertion fails as name and home.address.street fields are populated for sherlock but not for noName.
assertThat(noName).usingRecursiveComparison()
.ignoringExpectedNullFields()
.isEqualTo(sherlock);RecursiveComparisonAssert to chain other methods.public SELF ignoringFields(String... fieldNamesToIgnore)
home.address.street.
The given fieldNamesToIgnore are matched against field names, not field values.
Example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person noName = new Person(null, 1.80);
noName.home.address.street = null;
noName.home.address.number = 221;
// assertion succeeds as name and home.address.street fields are ignored in the comparison
assertThat(sherlock).usingRecursiveComparison()
.ignoringFields("name", "home.address.street")
.isEqualTo(noName);
// assertion fails as home.address.street fields differ and is not ignored.
assertThat(sherlock).usingRecursiveComparison()
.ignoringFields("name")
.isEqualTo(noName);fieldNamesToIgnore - the field names of the object under test to ignore in the comparison.RecursiveComparisonAssert to chain other methods.public SELF ignoringFieldsMatchingRegexes(String... regexes)
Nested fields can be specified by using dots like this: home\.address\.street (\ is used to escape
dots since they have a special meaning in regexes).
Example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person noName = new Person(null, 1.80);
noName.home.address.street = "Butcher Street";
noName.home.address.number = 222;
// assertion succeeds as name and all home fields are ignored in the comparison
assertThat(sherlock).usingRecursiveComparison()
.ignoringFieldsMatchingRegexes("n.me", "home.*")
.isEqualTo(noName);
// although home fields are ignored, assertion fails as name fields differ.
assertThat(sherlock).usingRecursiveComparison()
.ignoringFieldsMatchingRegexes("home.*")
.isEqualTo(noName);regexes - regexes used to ignore fields in the comparison.RecursiveComparisonAssert to chain other methods.public RecursiveComparisonAssert<?> ignoringFieldsOfTypes(Class<?>... typesToIgnore)
If some object under test fields are null it is not possible to evaluate their types unless in strictTypeChecking mode,
in that case the corresponding expected field's type is evaluated instead but if strictTypeChecking mode is disabled then null fields are not ignored.
Example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person sherlock2 = new Person("Sherlock", 1.90);
sherlock2.home.address.street = "Butcher Street";
sherlock2.home.address.number = 221;
// assertion succeeds as we ignore Address and height
assertThat(sherlock).usingRecursiveComparison()
.ignoringFieldsOfTypes(double.class, Address.class)
.isEqualTo(sherlock2);
// now this assertion fails as expected since the home.address.street fields and height differ
assertThat(sherlock).usingRecursiveComparison()
.isEqualTo(sherlock2);typesToIgnore - the types we want to ignore in the object under test fields.RecursiveComparisonAssert to chain other methods.public SELF ignoringAllOverriddenEquals()
Since 3.17.0 this is the default behavior for recursive comparisons, to revert to the previous behavior call usingOverriddenEquals().
Example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
// only compares number, ouch!
@Override
public boolean equals(final Object other) {
if (!(other instanceof Address)) return false;
Address castOther = (Address) other;
return Objects.equals(number, castOther.number);
}
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person sherlock2 = new Person("Sherlock", 1.80);
sherlock2.home.address.street = "Butcher Street";
sherlock2.home.address.number = 221;
// Assertion succeeds because:
// - overridden equals are used
// - Address has overridden equals and does not compare street fields.
assertThat(sherlock).usingRecursiveComparison()
.usingOverriddenEquals()
.isEqualTo(sherlock2);
// To avoid using Address overridden equals, don't call usingOverriddenEquals() or call ignoringAllOverriddenEquals()
// (calling ignoringAllOverriddenEquals() is actually not required as this is the default behavior).
// This assertion fails as it will compare home.address.street fields which differ
assertThat(sherlock).usingRecursiveComparison()
//.ignoringAllOverriddenEquals() // not needed as this is the default
.isEqualTo(sherlock2);RecursiveComparisonAssert to chain other methods.public SELF usingOverriddenEquals()
This method instructs the recursive comparison to use overridden equals.
Example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
// only compares number!
@Override
public boolean equals(final Object other) {
if (!(other instanceof Address)) return false;
Address castOther = (Address) other;
return Objects.equals(number, castOther.number);
}
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person sherlock2 = new Person("Sherlock", 1.80);
sherlock2.home.address.street = "Butcher Street";
sherlock2.home.address.number = 221;
// assertion succeeds because Address equals does not compare street fields.
assertThat(sherlock).usingRecursiveComparison()
.usingOverriddenEquals()
.isEqualTo(sherlock2);
// Assertion fails because:
// - Address equals is not used.
// - street fields are compared and differ.
assertThat(sherlock).usingRecursiveComparison()
.isEqualTo(sherlock2);RecursiveComparisonAssert to chain other methods.public SELF ignoringOverriddenEqualsForFields(String... fields)
equals with usingOverriddenEquals(),
this method allows to ignore overridden equals for the given fields (it adds them to the already registered ones).
Since 3.17.0 all overridden equals so this method is only relevant if you have called usingOverriddenEquals() before.
Nested fields can be specified by using dots like this: home.address.street
Example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
// only compares number
@Override
public boolean equals(final Object other) {
if (!(other instanceof Address)) return false;
Address castOther = (Address) other;
return Objects.equals(number, castOther.number);
}
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person sherlock2 = new Person("Sherlock", 1.80);
sherlock2.home.address.street = "Butcher Street";
sherlock2.home.address.number = 221;
// Assertion succeeds because:
// - overridden equals are used
// - Address has overridden equals and does not compare street fields.
assertThat(sherlock).usingRecursiveComparison()
.usingOverriddenEquals()
.isEqualTo(sherlock2);
// ignoringOverriddenEqualsForFields force a recursive comparison on the given field
// Assertion fails because:
// - Address equals is not used.
// - street fields are compared and differ.
assertThat(sherlock).usingRecursiveComparison()
.usingOverriddenEquals()
.ignoringOverriddenEqualsForFields("home.address")
.isEqualTo(sherlock2);fields - the fields we want to force a recursive comparison on.RecursiveComparisonAssert to chain other methods.public SELF ignoringOverriddenEqualsForTypes(Class<?>... types)
equals methods to compare fields,
this method allows to force a recursive comparison for all fields of the given types (it adds them to the already registered ones).
Since 3.17.0 all overridden equals so this method is only relevant if you have called usingOverriddenEquals() before.
Example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
// only compares number, ouch!
@Override
public boolean equals(final Object other) {
if (!(other instanceof Address)) return false;
Address castOther = (Address) other;
return Objects.equals(number, castOther.number);
}
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person sherlock2 = new Person("Sherlock", 1.80);
sherlock2.home.address.street = "Butcher Street";
sherlock2.home.address.number = 221;
// Assertion succeeds because:
// - overridden equals are used
// - Address has overridden equals and does not compare street fields.
assertThat(sherlock).usingRecursiveComparison()
.usingOverriddenEquals()
.isEqualTo(sherlock2);
// ignoringOverriddenEqualsForTypes force a recursive comparison on the given types.
// Assertion fails because:
// - Address equals is not used.
// - street fields are compared and differ.
assertThat(sherlock).usingRecursiveComparison()
.usingOverriddenEquals()
.ignoringOverriddenEqualsForTypes(Address.class)
.isEqualTo(sherlock2);types - the types we want to force a recursive comparison on.RecursiveComparisonAssert to chain other methods.public SELF ignoringOverriddenEqualsForFieldsMatchingRegexes(String... regexes)
equals with usingOverriddenEquals(),
this method allows to force a recursive comparison for the fields matching the given regexes (it adds them to the already registered ones).
Since 3.17.0 all overridden equals so this method is only relevant if you have called usingOverriddenEquals() before.
Nested fields can be specified by using dots like: home\.address\.street (\ is used to escape
dots since they have a special meaning in regexes).
Example:
public class Person {
String name;
double height;
Home home = new Home();
}
public class Home {
Address address = new Address();
}
public static class Address {
int number;
String street;
// only compares number, ouch!
@Override
public boolean equals(final Object other) {
if (!(other instanceof Address)) return false;
Address castOther = (Address) other;
return Objects.equals(number, castOther.number);
}
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person sherlock2 = new Person("Sherlock", 1.80);
sherlock2.home.address.street = "Butcher Street";
sherlock2.home.address.number = 221;
// assertion succeeds because overridden equals are used and thus street fields are mot compared
assertThat(sherlock).usingRecursiveComparison()
.usingOverriddenEquals()
.isEqualTo(sherlock2);
// ignoringOverriddenEqualsForFields force a recursive comparison on the field matching the regex
// now this assertion fails as we expect since the home.address.street fields differ
assertThat(sherlock).usingRecursiveComparison()
.usingOverriddenEquals()
.ignoringOverriddenEqualsForFieldsMatchingRegexes("home.*")
.isEqualTo(sherlock2);regexes - regexes used to specify the fields we want to force a recursive comparison on.RecursiveComparisonAssert to chain other methods.public SELF ignoringCollectionOrder()
Example:
public class Person {
String name;
List<Person> friends = new ArrayList<>();
}
Person sherlock1 = new Person("Sherlock Holmes");
sherlock1.friends.add(new Person("Dr. John Watson"));
sherlock1.friends.add(new Person("Molly Hooper"));
Person sherlock2 = new Person("Sherlock Holmes");
sherlock2.friends.add(new Person("Molly Hooper"));
sherlock2.friends.add(new Person("Dr. John Watson"));
// assertion succeeds as all fields collection order is ignored in the comparison
assertThat(sherlock1).usingRecursiveComparison()
.ignoringCollectionOrder()
.isEqualTo(sherlock2);
// assertion fails as fields collection order is not ignored in the comparison
assertThat(sherlock1).usingRecursiveComparison()
.isEqualTo(sherlock2);RecursiveComparisonAssert to chain other methods.public SELF ignoringCollectionOrderInFields(String... fieldsToIgnoreCollectionOrder)
home.address.street.
Example:
public class Person {
String name;
List<Person> friends = new ArrayList<>();
List<Person> enemies = new ArrayList<>();
}
Person sherlock1 = new Person("Sherlock Holmes");
sherlock1.friends.add(new Person("Dr. John Watson"));
sherlock1.friends.add(new Person("Molly Hooper"));
sherlock1.enemies.add(new Person("Jim Moriarty"));
sherlock1.enemies.add(new Person("Irene Adler"));
Person sherlock2 = new Person("Sherlock Holmes");
sherlock2.friends.add(new Person("Molly Hooper"));
sherlock2.friends.add(new Person("Dr. John Watson"));
sherlock2.enemies.add(new Person("Irene Adler"));
sherlock2.enemies.add(new Person("Jim Moriarty"));
// assertion succeeds as friends and enemies fields collection order is ignored in the comparison
assertThat(sherlock1).usingRecursiveComparison()
.ignoringCollectionOrderInFields("friends", "enemies")
.isEqualTo(sherlock2);
// assertion fails as enemies field collection order differ and it is not ignored
assertThat(sherlock1).usingRecursiveComparison()
.ignoringCollectionOrderInFields("friends")
.isEqualTo(sherlock2);fieldsToIgnoreCollectionOrder - the fields of the object under test to ignore collection order in the comparison.RecursiveComparisonAssert to chain other methods.public SELF ignoringCollectionOrderInFieldsMatchingRegexes(String... regexes)
Nested fields can be specified by using dots like this: home\.address\.street (\ is used to escape
dots since they have a special meaning in regexes).
Example:
public class Person {
String name;
List<Person> friends = new ArrayList<>();
List<Person> enemies = new ArrayList<>();
}
Person sherlock1 = new Person("Sherlock Holmes");
sherlock1.friends.add(new Person("Dr. John Watson"));
sherlock1.friends.add(new Person("Molly Hooper"));
sherlock1.enemies.add(new Person("Jim Moriarty"));
sherlock1.enemies.add(new Person("Irene Adler"));
Person sherlock2 = new Person("Sherlock Holmes");
sherlock2.friends.add(new Person("Molly Hooper"));
sherlock2.friends.add(new Person("Dr. John Watson"));
sherlock2.enemies.add(new Person("Irene Adler"));
sherlock2.enemies.add(new Person("Jim Moriarty"));
// assertion succeeds as friends and enemies fields collection order is ignored in the comparison
assertThat(sherlock1).usingRecursiveComparison()
.ignoringCollectionOrderInFieldsMatchingRegexes("friend.", "enemie.")
.isEqualTo(sherlock2);
// assertion fails as enemies field collection order differ and it is not ignored
assertThat(sherlock1).usingRecursiveComparison()
.ignoringCollectionOrderInFields("friend.")
.isEqualTo(sherlock2);regexes - regexes used to find the object under test fields to ignore collection order in the comparison.RecursiveComparisonAssert to chain other methods.public SELF withStrictTypeChecking()
Examples:
public class Person {
String name;
double height;
Person bestFriend;
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.bestFriend = new Person("Watson", 1.70);
Person sherlockClone = new Person("Sherlock", 1.80);
sherlockClone.bestFriend = new Person("Watson", 1.70);
// assertion succeeds as sherlock and sherlockClone have the same data and types
assertThat(sherlock).usingRecursiveComparison()
.withStrictTypeChecking()
.isEqualTo(sherlockClone);
// Let's now define a data structure similar to Person
public class PersonDTO {
String name;
double height;
PersonDTO bestFriend;
}
PersonDTO sherlockDto = new PersonDTO("Sherlock", 1.80);
sherlockDto.bestFriend = new PersonDTO("Watson", 1.70);
// assertion fails as Person and PersonDTO are not compatible even though they have the same data
assertThat(sherlock).usingRecursiveComparison()
.withStrictTypeChecking()
.isEqualTo(noName);
// Let's define a subclass of Person
public class Detective extends Person {
boolean busy;
}
Detective detectiveSherlock = new Detective("Sherlock", 1.80);
detectiveSherlock.bestFriend = new Person("Watson", 1.70);
detectiveSherlock.busy = true;
// assertion succeeds as Detective inherits from Person and
// only Person's fields are included into the comparison.
assertThat(sherlock).usingRecursiveComparison()
.withStrictTypeChecking()
.isEqualTo(detectiveSherlock);RecursiveComparisonAssert to chain other methods.public SELF withEqualsForFields(BiPredicate<?,?> equals, String... fieldLocations)
BiPredicate to compare fields with the given locations.
A typical usage is for comparing double/float fields with a given precision.
BiPredicates specified with this method have precedence over the ones registered with withEqualsForType(BiPredicate, Class)
or the comparators registered with withComparatorForType(Comparator, Class).
Note that registering a BiPredicate for a given field will override the previously registered Comparator (if any).
The field locations must be specified from the root object,
for example if Foo has a Bar field which has an id, one can register to a comparator for Bar's id by calling:
withEqualsForFields(idBiPredicate, "foo.id", "foo.bar.id")
Complete example:
public class TolkienCharacter {
String name;
double height;
}
TolkienCharacter frodo = new TolkienCharacter("Frodo", 1.2);
TolkienCharacter tallerFrodo = new TolkienCharacter("Frodo", 1.3);
TolkienCharacter reallyTallFrodo = new TolkienCharacter("Frodo", 1.9);
BiPredicate<Double, Double> closeEnough = (d1, d2) -> Math.abs(d1 - d2) <= 0.5;
// assertion succeeds
assertThat(frodo).usingRecursiveComparison()
.withEqualsForFields(closeEnough, "height")
.isEqualTo(tallerFrodo);
// assertion fails
assertThat(frodo).usingRecursiveComparison()
.withEqualsForFields(closeEnough, "height")
.isEqualTo(reallyTallFrodo);equals - the BiPredicate to use to compare the given fieldsfieldLocations - the location from the root object of the fields the BiPredicate should be used forRecursiveComparisonAssert to chain other methods.NullPointerException - if the given BiPredicate is null.public SELF withComparatorForFields(Comparator<?> comparator, String... fieldLocations)
Comparators registered with this method have precedence over comparators registered with withComparatorForType(Comparator, Class)
or BiPredicate registered with withEqualsForType(BiPredicate, Class).
The field locations must be specified from the root object,
for example if Foo has a Bar field which has an id, one can register to a comparator for Bar's id by calling:
withComparatorForFields(idComparator, "foo.id", "foo.bar.id")
Note that registering a Comparator for a given field will override the previously registered BiPredicate/Comparator (if any).
Complete example:
public class TolkienCharacter {
String name;
double height;
}
TolkienCharacter frodo = new TolkienCharacter("Frodo", 1.2);
TolkienCharacter tallerFrodo = new TolkienCharacter("Frodo", 1.3);
TolkienCharacter reallyTallFrodo = new TolkienCharacter("Frodo", 1.9);
Comparator<Double> closeEnough = (d1, d2) -> Math.abs(d1 - d2) <= 0.5 ? 0 : 1;
// assertion succeeds
assertThat(frodo).usingRecursiveComparison()
.withComparatorForFields(closeEnough, "height")
.isEqualTo(tallerFrodo);
// assertion fails
assertThat(frodo).usingRecursiveComparison()
.withComparatorForFields(closeEnough, "height")
.isEqualTo(reallyTallFrodo);comparator - the Comparator to use to compare the given fieldsfieldLocations - the location from the root object of the fields the comparator should be used forRecursiveComparisonAssert to chain other methods.NullPointerException - if the given comparator is null.public <T> SELF withComparatorForType(Comparator<? super T> comparator, Class<T> type)
Comparators registered with this method have less precedence than comparators registered with withComparatorForFields(Comparator, String...)
or BiPredicate registered with withEqualsForFields(BiPredicate, String...).
Note that registering a Comparator for a given type will override the previously registered BiPredicate/Comparator (if any).
Example:
public class TolkienCharacter {
String name;
double height;
}
TolkienCharacter frodo = new TolkienCharacter("Frodo", 1.2);
TolkienCharacter tallerFrodo = new TolkienCharacter("Frodo", 1.3);
TolkienCharacter reallyTallFrodo = new TolkienCharacter("Frodo", 1.9);
Comparator<Double> closeEnough = (d1, d2) -> Math.abs(d1 - d2) <= 0.5 ? 0 : 1;
// assertion succeeds
assertThat(frodo).usingRecursiveComparison()
.withComparatorForType(closeEnough, Double.class)
.isEqualTo(tallerFrodo);
// assertion fails
assertThat(frodo).usingRecursiveComparison()
.withComparatorForType(closeEnough, Double.class)
.isEqualTo(reallyTallFrodo);T - the class type to register a comparator forcomparator - the Comparator to use to compare the given fieldstype - the type to be compared with the given comparator.RecursiveComparisonAssert to chain other methods.NullPointerException - if the given comparator is null.public <T> SELF withEqualsForType(BiPredicate<? super T,? super T> equals, Class<T> type)
BiPredicate to compare the fields with the given type.
A typical usage is for comparing double/float fields with a given precision.
BiPredicates registered with this method have less precedence than the one registered with withEqualsForFields(BiPredicate, String...)
or comparators registered with withComparatorForFields(Comparator, String...).
Note that registering a BiPredicate for a given type will override the previously registered BiPredicate/Comparator (if any).
Example:
public class TolkienCharacter {
String name;
double height;
}
TolkienCharacter frodo = new TolkienCharacter("Frodo", 1.2);
TolkienCharacter tallerFrodo = new TolkienCharacter("Frodo", 1.3);
TolkienCharacter reallyTallFrodo = new TolkienCharacter("Frodo", 1.9);
BiPredicate<Double, Double> closeEnough = (d1, d2) -> Math.abs(d1 - d2) <= 0.5;
// assertion succeeds
assertThat(frodo).usingRecursiveComparison()
.withEqualsForType(closeEnough, Double.class)
.isEqualTo(tallerFrodo);
// assertion fails
assertThat(frodo).usingRecursiveComparison()
.withEqualsForType(closeEnough, Double.class)
.isEqualTo(reallyTallFrodo);T - the class type to register a BiPredicate forequals - the BiPredicate to use to compare the given fieldstype - the type to be compared with the given comparator.RecursiveComparisonAssert to chain other methods.NullPointerException - if the given BiPredicate is null.public SELF withErrorMessageForFields(String message, String... fieldLocations)
The fields must be specified from the root object, for example if Foo has a Bar field and both
have an id field, one can register a message for Foo and Bar's id by calling:
withErrorMessageForFields("some message", "foo.id", "foo.bar.id")
Messages registered with this method have precedence over the ones registered with withErrorMessageForType(String, Class).
In case of null as message the default error message will be used (See
ComparisonDifference.DEFAULT_TEMPLATE).
Example:
public class TolkienCharacter {
String name;
double height;
}
TolkienCharacter frodo = new TolkienCharacter("Frodo", 1.2);
TolkienCharacter tallerFrodo = new TolkienCharacter("Frodon", 1.4);
String message = "The field 'height' differ.";
// assertion fails
assertThat(frodo).usingRecursiveComparison()
.withErrorMessageForFields(message, "height")
.isEqualTo(tallerFrodo);
and the error will report the height field with the given overridden message instead of the one computed by AssertJ as with the name error:
Expecting actual:
TolkienCharacter [name=Frodo, height=1.2]
to be equal to:
TolkienCharacter [name=Frodon, height=1.4]
when recursively comparing field by field, but found the following 2 differences:
The field 'height' differ.
field/property 'name' differ:
- actual value : "Frodo"
- expected value: "Frodon"
The recursive comparison was performed with this configuration:
- no overridden equals methods were used in the comparison (except for java types)
- these types were compared with the following comparators:
- java.lang.Double -> DoubleComparator[precision=1.0E-15]
- java.lang.Float -> FloatComparator[precision=1.0E-6]
- java.nio.file.Path -> lexicographic comparator (Path natural order)
- actual and expected objects and their fields were compared field by field recursively even if they were not of the same type, this allows for example to compare a Person to a PersonDto (call strictTypeChecking(true) to change that behavior).
- these fields had overridden error messages:
- heightmessage - the error message that will be thrown when comparison error occurred.fieldLocations - the fields the error message should be used for.RecursiveComparisonAssert to chain other methods.public SELF withErrorMessageForType(String message, Class<?> type)
Message registered with this method have less precedence than the ones registered with withErrorMessageForFields(String, String...).
In case of null as message the default error message will be used (See
ComparisonDifference.DEFAULT_TEMPLATE).
Example:
public class TolkienCharacter {
String name;
double height;
}
TolkienCharacter frodo = new TolkienCharacter("Frodo", 1.2);
TolkienCharacter tallerFrodo = new TolkienCharacter("Frodon", 1.4);
String message = "Double field differ.";
// assertion fails
assertThat(frodo).usingRecursiveComparison()
.withErrorMessageForType(message, Double.class)
.isEqualTo(tallerFrodo);
and the error will report the height field with the given overridden message instead of the one computed by AssertJ as with the name error:
Expecting actual:
TolkienCharacter [name=Frodo, height=1.2]
to be equal to:
TolkienCharacter [name=Frodon, height=1.4]
when recursively comparing field by field, but found the following 2 differences:
Double field differ.
field/property 'name' differ:
- actual value : "Frodo"
- expected value: "Frodon"
The recursive comparison was performed with this configuration:
- no overridden equals methods were used in the comparison (except for java types)
- these types were compared with the following comparators:
- java.lang.Double -> DoubleComparator[precision=1.0E-15]
- java.lang.Float -> FloatComparator[precision=1.0E-6]
- java.nio.file.Path -> lexicographic comparator (Path natural order)
- actual and expected objects and their fields were compared field by field recursively even if they were not of the same type, this allows for example to compare a Person to a PersonDto (call strictTypeChecking(true) to change that behavior).
- these types had overridden error messages:
- heightmessage - the error message that will be thrown when comparison error occurred.type - the type the error message should be used for.RecursiveComparisonAssert to chain other methods.public RecursiveComparisonConfiguration getRecursiveComparisonConfiguration()
RecursiveComparisonConfiguration currently used.RecursiveComparisonConfiguration currently usedCopyright © 2025. All rights reserved.