【问题标题】:How does Spock deal with equality of Mocked objectsSpock如何处理Mocked对象的相等性
【发布时间】:2014-08-28 15:28:27
【问题描述】:

我正在测试使用库对象的 SortedSet 的类的行为(常规类不是接口,所以我引入了 cglib-nodep)。当排序集有多个对象时,我需要测试类的行为。 Library 对象已以这种方式模拟:

Library library = Mock()
Library library2 = Mock()

然后,我创建一个 TreeSet:

def libraries = [library, library2] as TreeSet

并调用被测系统方法:

sut.doStuff(libraries).

当我调试这个测试时,我看到库是一个只有一个元素的 SortedSet。这似乎是 Spock 处理平等方式的结果,因为:

def "equality test"() {
    expect:
        library == library2
}

当我运行测试时通过。有没有办法解决这种行为?

编辑:将 = 更改为 == 因为我无法输入

【问题讨论】:

  • library = library2 已通过,因为它是分配而非比较?
  • 是的,很抱歉,只是打字错误,我实际上并没有在测试中使用作业
  • 你试过library.is library2吗?不确定Library 是什么以及如何实现equals()hashCode()compareTo()

标签: unit-testing groovy spock


【解决方案1】:

做了一些研究。看看以下一组测试(groovy 控制台脚本):

@Grab('org.spockframework:spock-core:0.7-groovy-2.0')
@Grab('cglib:cglib-nodep:3.1')

import spock.lang.*

class Test extends Specification {
    def "not comparable mocks are not equal"() {
        given:
        def a1 = Mock(A)
        def a2 = Mock(A)

        expect:
        a1.hashCode() != a2.hashCode()
        !a1.equals(a2)
        !(a1 == a2)        
    }

    def "comparable mocks are not equal"() {
        given:
        def a1 = Mock(AC)
        def a2 = Mock(AC)

        expect:
        a1.hashCode() != a2.hashCode()
        !a1.equals(a2)
        !(a1 == a2)        
    }

    def "cannot create TreeSet when POJOs are not comparable"() {
        given:
        def a1 = Mock(A)
        def a2 = Mock(A)

        and:    
        a1.hashCode() != a2.hashCode()
        !a1.equals(a2)
        !(a1 == a2)     

        when:
        new TreeSet([a1,a2])

        then:
        def e = thrown(ClassCastException)
        e.message.endsWith('cannot be cast to java.lang.Comparable')
    } 

    def "there's a problem with Comparable Mocks"() {
         given:
        def a1 = Mock(AC)
        def a2 = Mock(AC)

        and:    
        a1.hashCode() != a2.hashCode()
        !a1.equals(a2)
        !(a1 == a2)     

        when:
        def s = new TreeSet([a1,a2])

        then:
        s.size() == 2
    }

    def "with HashSet it works as expected"() {
        given:
        def a1 = Mock(AC) 
        def a2 = Mock(AC) 

        and:    
        a1.hashCode() != a2.hashCode()
        !a1.equals(a2)
        !(a1 == a2)     

        when:
        def s = new HashSet([a1,a2])

        then:
        s.size() == 2
    }
}

class A {}

class AC implements Comparable {

    int compareTo(Object o) {
        1 //whatever value may be here, it's not called
    }
}

一般来说,当对象实现Comparable 接口时会出现问题。

  1. 第一个测试表明,相同对象的不同模拟具有不同的哈希码,并且不相等。在我看来,这是预期的行为。
  2. 第二个测试检查相同的条件,除了对象是Comparable。它失败。在我看来,这不是预期的行为。
  3. 第三个测试说明不可能使用不可比较的 POJO 创建TreeSet。预期行为。
  4. 第四个测试说明了你提到的问题。使用两个 Comparable 模拟创建的 TreeSet 的大小预计为 2。失败,大小为 1。
  5. 第五个测试表明,当使用HashSet时,满足第四个测试的条件。

IMO 这不是spock 相关的问题。 Spock 将cglib 用于模拟对象,这是应该寻找解释的地方。

编辑

如果 compareTo() 方法被模拟对象覆盖,它可以正常工作:

@Grab('org.spockframework:spock-core:0.7-groovy-2.0')
@Grab('cglib:cglib-nodep:3.1')

import spock.lang.*

class Test extends Specification {

    def "there's a problem with Comparable Mocks"() {
         given:
        def a1 = Mock(AC) {
            compareTo(_) >> 3145
        }
        def a2 = Mock(AC) {
            compareTo(_) >> 3146
        }

        and:    
        a1.hashCode() != a2.hashCode()
        !a1.equals(a2)
        !(a1 == a2)     

        when:
        def s = new TreeSet([a1,a2])

        then:
        s.size() == 2
    }
}

class AC implements Comparable {

    int compareTo(Object o) {
        1 //whatever value may be here, it's not called
    }
}

【讨论】:

  • 我不认为这是一个 cglib 问题。 Cglib 将始终委托compareTo 方法的调用。破坏代码的是委托实现,这可能是人们应该寻找解决方案的地方。
  • 不太了解 cglib 和 spock 的实现。感谢您的反馈,这只是一个镜头。
  • 也许 Spock 有一个后备方案,如果 Comparable 模拟可以相互比较,它会呈现相等。如果没有以不同方式指定,则模拟方法默认返回0,所以我猜是library.compareTo(library2) == 0 的调用。在这种情况下,相等性测试表现正确。或者 Groovy 对象的行为可能是这样的?我不是一个真正的 Groovy 人。
  • 我试图模拟 compareTo 方法,它总是返回 0 :/
  • 不应该。我直接在cglib中试了一下,效果很好。
【解决方案2】:

除非它的 equals() 方法被存根,否则 Mock() 只有 equals() 本身。但是,Groovy 的 == 并不总是意味着 equals()。对于Comparables,表示c1.compareTo(c2) == 0。同样,TreeSet 使用compareTo() 而不是equals 来确定相等性。潜在的解决方案:

  • 存根模拟的compareTo() 方法以返回合适的值。
  • 不要使用TreeSet
  • 使用以适当方式实现compareTo 的真实对象,而不是模拟。

PS:也许Mock(Comparable) 和/或Stub(Comparable) 应该以与equals() 一致的方式自动实现compareTo。您可以为此提出问题吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-18
    • 2019-04-02
    • 1970-01-01
    • 1970-01-01
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多