首页 文章

断言两个列表在Spock框架中是相同的

提问于
浏览
8

我使用Spock框架测试我的应用程序,测试是用Groovy编写的 .

作为一些方法评估的结果,我有一个对象列表 . 我想测试这个列表是否与我期望的列表相同 . 我编码了以下内容:

def expectedResults = [ ... ] //the list I expect to see
def isEqual = true;

when:
def realResults = getRealResultsMethod() //get real results in a list here
expectedResults.each {isEqual &= realResults.contains(it)}
then:
isEqual
0 * errorHandler.handleError(_) //by the way assert that my errorHandler is never called

这是我第一次使用Groovy的经历,所以可能是我错过了什么?

PS

令我困惑的是Groovy和Spock中的'equals'运算符 . 给定Java ArrayList或Java数组,equals运算符只是标识运算符:equals is == . 在Groovy中,据我所知,默认等于运算符实际上是等于(形式在这里:http://groovy.codehaus.org/Differences+from+Java) . 但对于Groovy List或Set,'equals'是什么?

UPDATE

更确切地说 . 我想找出两个列表有相同的对象,两个列表都没有额外的对象,顺序无关紧要 . 例如:

list=[1,5,8]

list1=[5,1,8]    
list2=[1,5,8,9]

println(list == list1) //should be equal, if we use == not equal    
println(list == list2) //should not be equal, if we use == not equal

4 回答

  • 0

    做就是了:

    when:
        def expectedResults = [ ... ]
        def realResults = getRealResultsMethod()
    
    then:
        realResults == expectedResults
    

    或者,如果你不关心订单(这违反了List的 Contract ,但你去了),你可以这样做:

    then:
        realResults.sort() == expectedResults.sort()
    

    或者将它们转换为集合或其他东西

  • 6

    如果您只需要检查两个列表是否具有相同的元素您可以尝试:

    when:
        def expectedResults = [ ... ]
        def realResults = getRealResultsMethod()
    
    then:
        realResults.size() == expectedResults.size()
        realResults.containsAll(expectedResults)
        expectedResults.containsAll(realResults)
    

    但是如果你需要检查两个列表是否相等你只需要(如在@tim_yates的响应中):

    when:
        def expectedResults = [ ... ]
        def realResults = getRealResultsMethod()
    
    then:
        realResults == expectedResults
    

    请记住,只有两个列表在同一 order 中具有相同的元素时才相同 .

  • -4

    您正在寻找的语义数据结构通常被称为包 . 在一个包中,如在一个集合中,元素的顺序无关紧要 . 但是,在包中,如在列表中,允许重复元素 . 因此,袋子相等包括具有相同数量的相同元素,尽管不一定是相同的顺序 . 因此,您正在寻找的是将"bag"语义应用于列表的方法 . 最简单的方法是复制其中一个袋子并从复制品中取出其他袋子的元素,直到:

    • 所有其他包's elements are exhausted and the duplicate is empty (they'等于!)

    • 所有其他包's elements are exhausted and the duplicate is NOT empty (they'与众不同!)

    • 在迭代过程中,另一个包中的一个被删除(它们是不同的!)

    equals() 实现的东西如下所示:

    class Bag {
        List list
        Bag(List list) { this.list = list }
        @Override boolean equals(that) {
            def thisList = list?.clone() ?: []
            that instanceof Bag &&
                (that?.list ?: []).every { thisList.remove((Object)it) } &&
                !thisList
        }
        @Override int hashCode() { this?.list?.sum { it?.hashCode() ?: 0 } ?: 0 }
        @Override String toString() { this?.list?.toString() }
    }
    
    def a = [1, 5, 1, -1, 8] as Bag
    def b = [5, 1, -1, 8, 1] as Bag // same elements different order
    def c = [1, 5, -1, 8]    as Bag // same elements different size
    def d = [5, 5, 1, -1, 8] as Bag // same elements same size different amounts of each
    
    assert a == b
    assert a != c
    assert a != d
    
    println a    // [1, 5, 1, -1, 8]
    println b    // [5, 1, -1, 8, 1]
    

    或者,如果您根本不关心原始列表顺序,则可以将包表示为 Map . bag元素值是map键,每个bag元素的出现次数是map值 . 那时,平等就是 Map 平等 .

    像这样:

    class BagAsMap {
        Map map = [:]
        BagAsMap(List list) {
            (list ?: []).each { map[it] = (map[it] ?: 0) + 1 }
        }
        @Override boolean equals(that) {
            that instanceof BagAsMap && this?.map == that?.map
        }
        @Override int hashCode() { this?.map?.hashCode() ?: 0 }
        @Override String toString() {
            '[' + map.keySet().sum { k -> (0..<(map[k])).sum { "${k}, " } }[0..-3] + ']'
        }
    }
    
    def a1 = [1, 5, 1, -1, 8] as BagAsMap
    def b1 = [5, 1, -1, 8, 1] as BagAsMap // same elements different order
    def c1 = [1, 5, -1, 8]    as BagAsMap // same elements different size
    def d1 = [5, 5, 1, -1, 8] as BagAsMap // same elements same size different amounts
    
    assert a1 == b1
    assert a1 != c1
    assert a1 != d1
    
    println a1
    println b1
    

    在任何情况下,如果您只需要检查一次或两次顺序中性列表等效,这是严重的过度杀伤,但如果经常需要包语义,那么以这两种方式之一定义Bag类可能是一个好主意 .

    如其他地方所述,在这种特定情况下, a.sort() == b.sort() 是一个足够的权宜之计,代替了完整的包语义 . 但是,并非所有可能放在列表中的对象都可以相互排序,即使使用最复杂的比较器闭包也是如此 . 但是,它们都有 hashCode()equals() ,这就是显示任何一个包实现所需的全部内容 .

    此外, List.sort() 具有O(n log n)算法复杂度,而所示的所有行包操作都是O(n)复杂度 . 不值得担心这些小清单,但对大型清单来说要大得多 .

  • 16
    list1.containsAll(list2) && list2.containsAll(list1)
    

相关问题