Java 断言两个列表在Spock框架中相等

Java 断言两个列表在Spock框架中相等,java,groovy,spock,Java,Groovy,Spock,我使用Spock框架测试我的应用程序,测试是用Groovy编写的 作为某种方法评估的结果,我有一个对象列表。我想测试这个列表是否与我期望的列表相同。我已编写了以下代码: def expectedResults = [ ... ] //the list I expect to see def isEqual = true; when: def realResults = getRealResultsMethod() //get real results in a list here expect

我使用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 array,equals运算符只是标识运算符:equals is==。据我所知,在Groovy中,默认的equals操作符实际上是equals(这里的形式是:)。但Groovy列表或集合的“等于”是什么

更新

更准确地说。 我想知道这两个列表是否有相同的对象,两个列表都没有额外的对象,顺序无关紧要。例如:

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
只要做:

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

then:
    realResults == expectedResults
或者,如果您不关心订单(这违反了列表的合同,但您可以这样做),您可以:

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

或者将它们转换为集合或其他东西,如果您只需要检查两个列表是否具有相同的元素,您可以尝试:

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

then:
    realResults.size() == expectedResults.size()
    realResults.containsAll(expectedResults)
    expectedResults.containsAll(realResults)
但如果你需要检查两个列表是否相等,你只需要(如@tim_yates的回答):


请记住,只有当两个列表具有相同的顺序的相同元素时,它们才是相等的。在一个袋子里,就像在一个集合里一样,元素的顺序并不重要。但是,在包中,如列表中,允许重复元素。因此,袋相等性包括具有相同的元素,每个元素具有相同的数量,尽管不一定具有相同的顺序。所以,似乎您正在寻找一种将“bag”语义应用于列表的方法。最简单的方法是复制其中一个袋子,并从副本中移除另一个袋子的元件,直到:

list1.containsAll(list2) && list2.containsAll(list1)
  • 其他包的所有元素都已耗尽,副本为空(它们相等!)
  • 其他包的所有元素都已耗尽,副本也不是空的(它们不同!)
  • 在迭代过程中,另一个包的元素之一无法从副本中删除(它们是不同的!)
类似于如下所示的
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]

或者,如果您根本不关心原始列表顺序,可以将包表示为地图。行李元素值为地图键,每个行李元素的出现次数为地图值。在这一点上,相等就是映射相等

像这样:

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
类BagAsMap{
Map=[:]
BagaMap(列表){
(列表?:[])。每个{map[it]=(map[it]?:0)+1}
}
@覆盖布尔等于(该值){
BagAsMap&&this?.map==that?.map的实例
}
@重写int hashCode(){this?.map?.hashCode()?:0}
@重写字符串toString(){

“['+map.keySet().sum{k->(0..很抱歉搞混了,tim_yates,我需要[1,5,8]等于[5,1,8],这就是我编写的代码,但可能有一个更简单的解决方案。@tim_-yates,这不起作用,因为列表可能包含不同顺序的元素。谢谢,@tim_-yates!但对于原语以外的情况,元素相等不能确保排序后的位置相等,对吗?我的意思是[o1,o2,o2']和[o1,o2',o2]其中o2==o2'都已排序,但list1!=list2
sort
只适用于Groovy知道如何比较的元素(主要是
compariable
对象)。更通用的解决方案是
list1 as Set==List2as Set
。为什么不使用
Set
?这还不够:
[1,1]。包含所有([1])
[1]都是正确的。containsAll([1,1])
根据您的标准,这不应该通过。关于断言containsAll()与集合上的==相比:IntelliJ/Spock中的==运算符提供了一个特殊功能,它包含所有未包含的功能:当==断言失败时,它在输出中提供了一个链接,该链接提供了一个非常好的“差异”视图。对于其他集合,您必须执行一些复制/粘贴操作,以便与断言的值并排查看输出。您需要同时检查containsAll。realResults=[1,2,3]和expectedResults=[1,2,2]通过,但不完全相同这仍然不太正确[1,1,2,3]和[1,2,2,3]将通过检查
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