How do I check for deep object equality with spock.
Lets say we have a super simple test that compares to identical person objects
def "A persons test"() {
setup:
def person1 = new Person("Foo", new Address("Bar"))
def person2 = new Person("Foo", new Address("Bar"))
expect:
person1 == person2
}
The test fails
Condition not satisfied:
person1 == person2
| | |
| | Person@6bedbc4d
| false
Person@57af006c
This looks like a very natural way of asserting equality.
One of the main reason to start using spock was to avoid having to write a lot of hamcrest boilerplate matchers code.
Spock has no built-in mechanism for performing deep Object comparison, because defining object equality is out of scope of any testing framework. You can do a various things.
If both your classes (Person
and Address
) are Groovy classes you can generate equals
and hashCode
methods using @EqualsAndHashCode
annotation over both classes, like:
import groovy.transform.EqualsAndHashCode
import groovy.transform.TupleConstructor
import spock.lang.Specification
class PersonSpec extends Specification {
def "a person test"() {
setup:
def person1 = new Person("Foo", new Address("Bar"))
def person2 = new Person("Foo", new Address("Bar"))
expect:
person1 == person2
}
@TupleConstructor
@EqualsAndHashCode
static class Person {
String name
Address address
}
@TupleConstructor
@EqualsAndHashCode
static class Address {
String city
}
}
This is just a convenient alternative for implementing both methods in Groovy.
If you want to compare both objects with ==
operator then you will have to define equals
and hashCode
methods in both classes, something like:
public final class Person {
private final String name;
private final Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (name != null ? !name.equals(person.name) : person.name != null) return false;
return address != null ? address.equals(person.address) : person.address == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (address != null ? address.hashCode() : 0);
return result;
}
static class Address {
private final String city;
public Address(String city) {
this.city = city;
}
public String getCity() {
return city;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return city != null ? city.equals(address.city) : address.city == null;
}
@Override
public int hashCode() {
return city != null ? city.hashCode() : 0;
}
}
}
In this example both methods were defined using IntelliJ IDEA "Generate equals and hashCode" command.
If you don't want to define both methods manually (because e.g. you have to remember to change them anytime you modify your class fields) then you can use Lombok's @EqualsAndHashCode
annotation that does something similar to Groovy's annotation, but can be applied to any Java class.
equals
and hashCode
methodsWell, in this case you can try various things:
You can try comparing both objects field-by-field, like:
class PersonSpec extends Specification {
def "a person test"() {
setup:
def person1 = new Person("Foo", new Address("Bar"))
def person2 = new Person("Foo", new Address("Bar"))
expect:
person1.name == person2.name
and:
person1.address.city == person2.address.city
}
@TupleConstructor
static class Person {
String name
Address address
}
@TupleConstructor
static class Address {
String city
}
}
You can try using 3rd party tools like Unitils reflection assertion
That may sound bizarre, but you can compare JSON representation of both objects, something like:
import groovy.json.JsonOutput
import groovy.transform.TupleConstructor
import spock.lang.Specification
class PersonSpec extends Specification {
def "a person test"() {
setup:
def person1 = new Person("Foo", new Address("Bar"))
def person2 = new Person("Foo", new Address("Bar"))
expect:
new JsonOutput().toJson(person1) == new JsonOutput().toJson(person2)
}
@TupleConstructor
static class Person {
String name
Address address
}
@TupleConstructor
static class Address {
String city
}
}
Anyway, I would definitely suggest defining equals
and hashCode
in one way or another and simply use ==
operator. Hope it helps.
You can take advantage of Groovy's succinct map comparison syntax:
person1.properties == person2.properties
That only works for simple flat objects, not nested ones. You could adapt it like so:
person1.properties << ['address': person1.address.properties] == person2.properties << ['address': person2.address.properties]
...but JSON solution is more elegant at that point.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With