groovy 在Spock框架中Assert两个列表相等

dojqjjoe  于 8个月前  发布在  其他
关注(0)|答案(5)|浏览(93)

我使用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运算符只是恒等运算符:等于==。在Groovy中,据我所知,默认的equals运算符实际上是equals(形式如下:http://groovy.codehaus.org/Differences+from+Java)。但是什么是Groovy List或Set的“equals”?
更新
更准确地说。我想知道这两个列表是否有相同的对象,两个列表都没有额外的对象,顺序无关紧要。举例来说:

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
az31mfrm

az31mfrm1#

只要做:

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

then:
    realResults == expectedResults

或者,如果你不关心顺序(这违反了List的约定,但你可以这样做),你可以这样做:

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

或者把它们转换成集合什么的

wswtfjt7

wswtfjt72#

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

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

请记住,两个列表只有在它们具有相同顺序的相同元素时才相等。

y4ekin9u

y4ekin9u3#

您正在寻找的语义数据结构通常被称为“包”。在包中,就像在集合中一样,元素的顺序并不重要。但是,在包中,就像在列表中一样,允许重复元素。因此,袋相等包括具有相同数量的相同元素,尽管不一定在相同的顺序中。因此,看起来您正在寻找的是一种将“bag”语义应用于列表的方法。最简单的方法是复制其中一个袋子,并从副本中删除另一个袋子的元素,直到:

  • 所有其他袋子的元素都用完了,副本是空的(它们相等!)
  • 所有其他袋子的元素都用完了,副本不是空的(它们是不同的!))
  • 在迭代过程中,另一个包的元素之一不能从副本中删除(它们是不同的!)

类似于下面所示的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语义,那么用这两种方式之一定义一个Bag类可能是一个好主意。
正如其他地方所指出的,在这个特定的情况下,a.sort() == b.sort()是一个足够的权宜之计,可以代替完整的包语义。然而,并不是所有可能放在一个列表中的对象都是可相互排序的,即使使用最复杂的比较器闭包也是如此。但是,它们都有hashCode()equals(),这是所示的两个包实现所需的全部内容。
此外,List.sort()具有 O(n log n) 算法复杂度,而所有显示的包操作都是 O(n) 复杂度。对于这些小名单不值得担心,但对于大名单来说,这是一个更大的问题。

nfs0ujit

nfs0ujit4#

自从Spock 2.1以来,内置了一个比其他答案中提到的更好的选项:=~==~运算符。查看更多文档
==~完全符合您的要求:两个列表只有当它们包含相同数量的相等对象时才相等,忽略顺序。
在您的案例中:

def list = [1,5,8]

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

list ==~ list1 // true
list ==~ list2 // false
ubof19bj

ubof19bj5#

list1.containsAll(list2) && list2.containsAll(list1)

相关问题