public class RecursiveComparisonAssert<SELF extends RecursiveComparisonAssert<SELF>> extends AbstractAssert<SELF,Object>
actual, info, myself, throwUnsupportedExceptionOnEquals
Constructor and Description |
---|
RecursiveComparisonAssert(Object actual,
RecursiveComparisonConfiguration recursiveComparisonConfiguration) |
Modifier and Type | Method and Description |
---|---|
RecursiveComparisonConfiguration |
getRecursiveComparisonConfiguration()
Returns the
RecursiveComparisonConfiguration currently used. |
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()
By default the recursive comparison uses overridden
equals methods to compare fields,
this method allows to compare recursively all fields except fields with java types (at some point we need to compare something!). |
SELF |
ignoringFields(String... fieldsToIgnore)
Makes the recursive comparison to ignore the given the object under test fields.
|
SELF |
ignoringFieldsMatchingRegexes(String... regexes)
Makes the recursive comparison to ignore the object under test fields matching the given regexes.
|
SELF |
ignoringOverriddenEqualsForFields(String... fields)
By default the recursive comparison uses overridden
equals methods to compare fields,
this method allows to force a recursive comparison for the given fields (it adds them to the already registered ones). |
SELF |
ignoringOverriddenEqualsForFieldsMatchingRegexes(String... regexes)
By default the recursive comparison uses overridden
equals methods to compare fields,
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 |
withComparatorForFields(Comparator<?> comparator,
String... fieldLocations)
Allows to register a specific comparator to compare fields with the given locations.
|
<T> SELF |
withComparatorForType(Comparator<? super T> comparator,
Class<T> type)
Allows to register a specific comparator to compare the fields with the given type.
|
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).
|
as, as, asList, asString, describedAs, describedAs, descriptionText, doesNotHave, doesNotHaveSameClassAs, equals, failWithMessage, getWritableAssertionInfo, has, hashCode, hasSameClassAs, hasSameHashCodeAs, hasToString, inBinary, inHexadecimal, is, isExactlyInstanceOf, isIn, isIn, isInstanceOf, isInstanceOfAny, isInstanceOfSatisfying, isNot, isNotEqualTo, isNotExactlyInstanceOf, isNotIn, isNotIn, isNotInstanceOf, isNotInstanceOfAny, isNotNull, isNotOfAnyClassIn, isNotSameAs, isNull, isOfAnyClassIn, isSameAs, matches, matches, newListAssertInstance, overridingErrorMessage, satisfies, satisfies, satisfiesAnyOf, satisfiesAnyOf, setCustomRepresentation, throwAssertionError, usingComparator, usingComparator, usingDefaultComparator, withFailMessage, withRepresentation, withThreadDumpOnError
public 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 fiels 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 per (nested) fields or type with the methods below (but before calling isEqualTo
otherwise this has no effect!):
withComparatorForFields(Comparator, String...)
for one or multiple fieldswithComparatorForType(Comparator, Class)
for a given typeNote 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 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 noName.
assertThat(sherlock).usingRecursiveComparison()
.ignoringActualNullFields()
.isEqualTo(noName);
RecursiveComparisonAssert
to chain other methods.public SELF ignoringFields(String... fieldsToIgnore)
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;
}
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);
fieldsToIgnore
- the fields 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 SELF ignoringAllOverriddenEquals()
equals
methods to compare fields,
this method allows to compare recursively all fields except fields with java types (at some point we need to compare something!).
For the recursive comparison to use the overridden equals
of a given type anyway (like Date
) you can register
a type comparator using withComparatorForType(Comparator, Class)
.
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 but that's not what we expected since the home.address.street fields differ
// but the equals implemenation in Address does not compare them.
assertThat(sherlock).usingRecursiveComparison()
.isEqualTo(sherlock2);
// to avoid the previous issue, we force a recursive comparison on the home.address field
// now this assertion fails as we expect since the home.address.street fields differ
assertThat(sherlock).usingRecursiveComparison()
.ignoringAllOverriddenEquals()
.isEqualTo(sherlock2);
RecursiveComparisonAssert
to chain other methods.public SELF ignoringOverriddenEqualsForFields(String... fields)
equals
methods to compare fields,
this method allows to force a recursive comparison for the given fields (it adds them to the already registered ones).
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, 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 but that's not what we expected since the home.address.street fields differ
// but the equals implemenation in Address does not compare them.
assertThat(sherlock).usingRecursiveComparison()
.isEqualTo(sherlock2);
// to avoid the previous issue, we force a recursive comparison on the home.address field
// now this assertion fails as we expect since the home.address.street fields differ
assertThat(sherlock).usingRecursiveComparison()
.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).
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 but that's not what we expected since the home.address.street fields differ
// but the equals implemenation in Address does not compare them.
assertThat(sherlock).usingRecursiveComparison()
.isEqualTo(sherlock2);
// to avoid the previous issue, we force a recursive comparison on the Address type
// now this assertion fails as we expect since the home.address.street fields differ
assertThat(sherlock).usingRecursiveComparison()
.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
methods to compare fields,
this method allows to force a recursive comparison for the fields matching the given regexes (it adds them to the already registered ones).
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 but that's not what we expected since the home.address.street fields differ
// but the equals implemenation in Address does not compare them.
assertThat(sherlock).usingRecursiveComparison()
.isEqualTo(sherlock2);
// to avoid the previous issue, we force a recursive comparison on home and its fields
// now this assertion fails as we expect since the home.address.street fields differ
assertThat(sherlock).usingRecursiveComparison()
.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 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 withComparatorForFields(Comparator<?> comparator, String... fieldLocations)
Comparators specified by this method have precedence over comparators added with withComparatorForType(Comparator, 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:
withComparatorForField("bar.id", idComparator)
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;
// assertions succeed
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.public <T> SELF withComparatorForType(Comparator<? super T> comparator, Class<T> type)
Comparators specified by this method have less precedence than comparators added with withComparatorForFields(Comparator, String...)
.
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;
// assertions succeed
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.public RecursiveComparisonConfiguration getRecursiveComparisonConfiguration()
RecursiveComparisonConfiguration
currently used.RecursiveComparisonConfiguration
currently usedCopyright © 2014–2019 AssertJ. All rights reserved.