Spock如何处理Mocked对象的相等性

时间:2014-08-28 15:28:28

标签: unit-testing groovy spock

我正在测试使用Library对象的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
}
当我运行测试时,

通过。有没有办法可以绕过这种行为?

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

2 个答案:

答案 0 :(得分:3)

做了一些研究。看看下面的一组测试(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. 第四次测试说明了你提到的问题。使用两个TreeSet模拟创建的Comparable的大小预计为2.它失败,大小为1.
  5. 第五项测试显示,使用HashSet时,符合4项测试的条件。
  6. IMO这不是spock相关的问题。 Spock使用cglib作为mockin对象,这是应该寻找解释的地方。

    修改

    如果模拟对象覆盖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
        }
    }
    

答案 1 :(得分:3)

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

  • 捣乱嘲笑' compareTo()方法返回合适的值。
  • 请勿使用TreeSet
  • 使用以合适的方式实现compareTo而不是模拟的真实对象。

PS:Mock(Comparable)和/或Stub(Comparable)可能会以与compareTo一致的方式自动实施equals()。你可以为此提出问题吗?