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.
1. Both classes are Groovy classes
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.
2. Both classes are Java classes
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.
3. I can use Lombok!
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.
4. I want to keep default equals and hashCode methods
Well, 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.