簡體   English   中英

斷言兩個列表在Spock框架中是相同的

[英]Assert two lists are equal in Spock framework

我使用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的'等於'是什么?

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

做就是了:

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

then:
    realResults == expectedResults

或者,如果你不關心訂單(這違反了List的合同,但你去了),你可以這樣做:

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

或者將它們轉換為集合或其他東西

如果您只需要檢查兩個列表是否具有相同的元素您可以嘗試:

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

請記住,只有兩個列表在相同的順序中具有相同的元素時才相等。

您正在尋找的語義數據結構通常被稱為 在一個包中,如在一個集合中,元素的順序無關緊要。 但是,在包中,如在列表中,允許重復元素。 因此,袋子相等包括具有相同數量的相同元素,盡管不一定是相同的順序。 因此,您正在尋找的是將“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]

或者,如果您根本不關心原始列表順序,則可以將包表示為地圖。 bag元素值是map鍵,每個bag元素的出現次數是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)算法復雜度,而所示的所有bag操作都是O(n)復雜度。 不值得擔心這些小清單,但對大型清單來說要大得多。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM