【问题标题】:Is object deep-compare possible with Spock Framework?Spock 框架是否可以进行对象深度比较?
【发布时间】:2017-11-19 08:37:37
【问题描述】:

如何使用 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 样板匹配器代码。

【问题讨论】:

    标签: groovy equality spock


    【解决方案1】:

    Spock 没有用于执行深度对象比较的内置机制,因为定义对象相等性超出了任何测试框架的范围。你可以做各种各样的事情。

    1。这两个类都是 Groovy 类

    如果您的两个类(PersonAddress)都是 Groovy 类,您可以在两个类上使用 @EqualsAndHashCode 注释生成 equalshashCode 方法,例如:

    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。这两个类都是 Java 类

    如果您想用== 运算符比较这两个对象,那么您必须在两个类中定义equalshashCode 方法,例如:

    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“生成等于和哈希码”命令定义的。

    3。我可以使用龙目岛!

    如果您不想手动定义这两种方法(例如,您必须记住在修改类字段时随时更改它们),那么您可以使用Lombok's @EqualsAndHashCode annotation,它执行类似于 Groovy 注释的操作,但可以应用到任何 Java 类。

    4。我想保留默认的equalshashCode 方法

    好吧,在这种情况下,您可以尝试各种方法:

    1. 您可以尝试逐个字段比较两个对象,例如:

      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
          }
      }
      
    2. 您可以尝试使用第三方工具,例如Unitils reflection assertion

    3. 这可能听起来很奇怪,但您可以比较两个对象的 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
          }
      }
      

    无论如何,我肯定会建议以一种或另一种方式定义equalshashCode,并简单地使用== 运算符。希望对您有所帮助。

    【讨论】:

    【解决方案2】:

    您可以利用 Groovy 简洁的地图比较语法:

    person1.properties == person2.properties
    

    这仅适用于简单的平面对象,不适用于嵌套对象。你可以这样调整它:

    person1.properties << ['address': person1.address.properties] == person2.properties << ['address': person2.address.properties]
    

    ...但 JSON 解决方案在这一点上更优雅。

    【讨论】:

      【解决方案3】:

      看来,您需要更正覆盖您的 equals 和 hashcode 方法。 在 Groovy 中它可以很容易地完成,您需要使用 @Canonical 注释。它为您提供了更多的平等和哈希码,购买方式。

      【讨论】:

        【解决方案4】:

        我强烈建议您使用 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"
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-11-29
          • 2016-09-01
          • 2015-10-19
          • 2015-06-10
          • 2016-09-10
          • 1970-01-01
          • 2021-12-31
          • 2018-02-28
          相关资源
          最近更新 更多