Spock Framework 可以进行对象深度比较吗?
Is object deep-compare possible with Spock Framework?
如何使用 spock 检查深度对象相等性。
假设我们有一个超级简单的测试,可以与相同的人对象进行比较
def "A persons test"() {
setup:
def person1 = new Person("Foo", new Address("Bar"))
def person2 = new Person("Foo", new Address("Bar"))
expect:
person1 == person2
}
测试失败
Condition not satisfied:
person1 == person2
| | |
| | Person@6bedbc4d
| false
Person@57af006c
这看起来像是一种非常自然的断言平等的方式。
开始使用 spock 的主要原因之一是避免编写大量 hamcrest 样板匹配器代码。
Spock 没有用于执行深度对象比较的内置机制,因为定义对象相等性超出了任何测试框架的范围。你可以做各种各样的事情。
1。两个 classes 都是 Groovy classes
如果您的 classes(Person
和 Address
)都是 Groovy classes 您可以生成 equals
和 hashCode
方法在两个 class 上使用 @EqualsAndHashCode
注释,例如:
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
}
}
这只是在 Groovy.
中实现这两种方法的一种方便的替代方法
2。两个 classes 都是 Java classes
如果你想用 ==
运算符比较两个对象,那么你必须在两个 class 中定义 equals
和 hashCode
方法,例如:
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;
}
}
}
在此示例中,两种方法都是使用 IntelliJ IDEA "Generate equals and hashCode" 命令定义的。
3。我可以使用龙目岛!
如果您不想手动定义这两种方法(因为例如您必须记住在修改 class 字段时更改它们),那么您可以使用 Lombok's @EqualsAndHashCode
annotation 做类似的事情Groovy 的注释,但可以应用于任何 Java class.
4。我想保留默认的 equals
和 hashCode
方法
好吧,在这种情况下,您可以尝试各种方法:
您可以尝试逐个比较两个对象,例如:
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
}
}
您可以尝试使用第三方工具 Unitils reflection assertion
这听起来可能很奇怪,但您可以比较两个对象的 JSON 表示,例如:
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
}
}
无论如何,我肯定会建议以一种或另一种方式定义 equals
和 hashCode
并简单地使用 ==
运算符。希望对你有帮助。
看来,您需要更正重写您的 equals 和 hashcode 方法。
在 Groovy 中它可以很容易地完成,你需要使用 @Canonical 注释。它给你的不止是equals和hashcode,买路吧。
您可以利用 Groovy 简洁的地图比较语法:
person1.properties == person2.properties
这仅适用于简单的平面对象,不适用于嵌套对象。您可以这样调整它:
person1.properties << ['address': person1.address.properties] == person2.properties << ['address': person2.address.properties]
...但是 JSON 解决方案在这一点上更优雅。
我强烈建议您使用 Assertj 进行深度断言。请参阅下面的示例:
def "labels1 should be deeply equal to labels2"() {
when:
def labels1 = [new Label("labelA"), new Label("labelB")]
def labels2 = [new Label("labelB"), new Label("labelA")]
then:
assertThat(labels1)
.usingRecursiveComparison()
.ignoringCollectionOrder()
.isEqualTo(labels2)
}
别忘了添加 Gradle 依赖项:
dependencies {
testImplementation "org.assertj:assertj-core:3.11.1"
}
如何使用 spock 检查深度对象相等性。
假设我们有一个超级简单的测试,可以与相同的人对象进行比较
def "A persons test"() {
setup:
def person1 = new Person("Foo", new Address("Bar"))
def person2 = new Person("Foo", new Address("Bar"))
expect:
person1 == person2
}
测试失败
Condition not satisfied:
person1 == person2
| | |
| | Person@6bedbc4d
| false
Person@57af006c
这看起来像是一种非常自然的断言平等的方式。
开始使用 spock 的主要原因之一是避免编写大量 hamcrest 样板匹配器代码。
Spock 没有用于执行深度对象比较的内置机制,因为定义对象相等性超出了任何测试框架的范围。你可以做各种各样的事情。
1。两个 classes 都是 Groovy classes
如果您的 classes(Person
和 Address
)都是 Groovy classes 您可以生成 equals
和 hashCode
方法在两个 class 上使用 @EqualsAndHashCode
注释,例如:
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
}
}
这只是在 Groovy.
中实现这两种方法的一种方便的替代方法2。两个 classes 都是 Java classes
如果你想用 ==
运算符比较两个对象,那么你必须在两个 class 中定义 equals
和 hashCode
方法,例如:
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;
}
}
}
在此示例中,两种方法都是使用 IntelliJ IDEA "Generate equals and hashCode" 命令定义的。
3。我可以使用龙目岛!
如果您不想手动定义这两种方法(因为例如您必须记住在修改 class 字段时更改它们),那么您可以使用 Lombok's @EqualsAndHashCode
annotation 做类似的事情Groovy 的注释,但可以应用于任何 Java class.
4。我想保留默认的 equals
和 hashCode
方法
好吧,在这种情况下,您可以尝试各种方法:
您可以尝试逐个比较两个对象,例如:
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 } }
您可以尝试使用第三方工具 Unitils reflection assertion
这听起来可能很奇怪,但您可以比较两个对象的 JSON 表示,例如:
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 } }
无论如何,我肯定会建议以一种或另一种方式定义 equals
和 hashCode
并简单地使用 ==
运算符。希望对你有帮助。
看来,您需要更正重写您的 equals 和 hashcode 方法。 在 Groovy 中它可以很容易地完成,你需要使用 @Canonical 注释。它给你的不止是equals和hashcode,买路吧。
您可以利用 Groovy 简洁的地图比较语法:
person1.properties == person2.properties
这仅适用于简单的平面对象,不适用于嵌套对象。您可以这样调整它:
person1.properties << ['address': person1.address.properties] == person2.properties << ['address': person2.address.properties]
...但是 JSON 解决方案在这一点上更优雅。
我强烈建议您使用 Assertj 进行深度断言。请参阅下面的示例:
def "labels1 should be deeply equal to labels2"() {
when:
def labels1 = [new Label("labelA"), new Label("labelB")]
def labels2 = [new Label("labelB"), new Label("labelA")]
then:
assertThat(labels1)
.usingRecursiveComparison()
.ignoringCollectionOrder()
.isEqualTo(labels2)
}
别忘了添加 Gradle 依赖项:
dependencies {
testImplementation "org.assertj:assertj-core:3.11.1"
}