Spock翻譯Data Driven Testing

Data Driven Testing

一般,執行相同的測試代碼屢次是有用的,在不一樣的輸入與預期結果。spock數據驅動測試支持使他成爲第一類級別特性sql

Introduction

Suppose we want to specify the behavior of the Math.max method:數據庫

class MathSpec extends Specification {
    def "maximum of two numbers"() {
        expect:
        // exercise math method for a few different inputs
        Math.max(1, 3) == 3
        Math.max(7, 4) == 7
        Math.max(0, 0) == 0
    }
}

儘管這種方式是好的在簡單的用例像這個,他有一些潛在的缺點:
代碼和數據時混合的,可是不容易獨立改變
數據不容易自動生成或者獲取從外部源
順序實施相同的代碼屢次,它或者已經被複制或者被提取到到一個分離的方法
在失敗的用例中,不能馬上清理失敗引發的輸入
實施相同的代碼屢次不利於從相同的隔離,做爲執行分離方法方式。
spock的數據驅動支持試圖解決這些問題。在開始以前,讓咱們重構上面的代碼使用數據驅動特性方法。首先,咱們介紹三個方法參數替換掉硬編碼interge 值。express

class MathSpec extends Specification {
    def "maximum of two numbers"(int a, int b, int c) {
        expect:
        Math.max(a, b) == c

        ...
    }
}

咱們完成這個邏輯測試,可是仍須要數據值被使用。這樣作where block 放置方法的最後。在這個簡單的用例,where:block持有數據表。dom

Data Tables

Data tables are a convenient way to exercise a feature method with a fixed set of data values:ide

class Math extends Specification {
    def "maximum of two numbers"(int a, int b, int c) {
        expect:
        Math.max(a, b) == c

        where:
        a | b | c
        1 | 3 | 3
        7 | 4 | 4
        0 | 0 | 0
    }
}

數據表是一個方便的方式實施一個特性方法使用一肯定組的數據。測試

where:
a | _
1 | _
7 | _
0 | _
表第一行,稱爲數據頭,定義了變量。子行,稱做數據行,持有相應的數據。對每一行,特性方法都會被執行一次。咱們稱做迭代的方法。若是一個迭代方法失敗了,意味着其餘迭代仍然被執行。全部的
失敗都會被報告。編碼

Isolated Execution of Iterations

迭代式獨立的從互相間相同的分割特性方法。每一個迭代獲取本身實例從spec類。並setup cleanup會被分別調用在每一個迭代執行先後。code

共享對象在迭代間
順序的共享一個對象在迭代間,它使用@Shared 或者靜態字段對象

提示
只有@Shared 和靜態字段 能被訪問在where:block裏面。
注意,這些對象也能被共享給其餘方法。不是一個好的方式共享對象在相同的方法迭代。若是你思考這個問題,思考放入每一個方法在隔離的spec,全部能被處理在相同的文件。這個實現更好的隔離在一些樣板代碼的成本。索引

Syntactic Variations

class DataDriven extends Specification {
    def "maximum of two numbers"() {
        expect:
        Math.max(a, b) == c

        where:
        a | b || c
        3 | 5 || 5
        7 | 0 || 7
        0 | 0 || 0
    }
}

上面的代碼能被調整在幾個方面。首先,從where:block 已經定義全部的數據變量,這個方法參數能被提交。其次,輸入與期待輸出能被分離使用雙線去虛擬設置分離。使用它,代碼變成這樣。

Reporting of Failures

讓咱們設想咱們實現max方法有一個錯誤,其中一個迭代失敗了。

兩個數字中最大的數字 失敗

明顯的問題是:迭代失敗,數據值時什麼。在咱們的例子裏,它很是難指出是第二個迭代失敗。在其餘時候多是更加困難甚至是不可能。在任何用例裏,若是spock能大聲並清晰哪次失敗是很是好的,
超過只是報告失敗。這是@Unroll註解的目標

Method Unrolling

一個方法註釋使用@Unroll 將會有迭代過程獨立的報告。
爲什麼@Unroll 不默值?
一個緣由是一些執行環境期待提早告訴測試方法的數量,而且肯定實際數量問題。寧一個緣由是該註解能極大改變測試報告數量,可能不是明智的。
注意unrolling沒有影響在方法怎麼樣執行上。他只是在報告裏交替。依賴執行環境,輸出像這樣

maximum of two numbers[0]   PASSED

maximum of two numbers[1]   FAILED

Math.max(a, b) == c
    |    |  |  |  |
    |    7  0  |  7
    42         false

maximum of two numbers[2] PASSED
This tells us that the second iteration (with index 1) failed. With a bit of effort, we can do even better:

@Unroll
def "maximum of #a and #b is #c"() { ... }

這個告訴咱們第二個迭代失敗,索引爲1,隨着一點點努力,咱們能作更好。
這個方法名使用佔位符,表示經過一前置一個#符號,關聯數據變量a b c,在輸出,佔位符將會被替換使用具體的值。

maximum of 3 and 5 is 5   PASSED
maximum of 7 and 0 is 7   FAILED

Math.max(a, b) == c
    |    |  |  |  |
    |    7  0  |  7
    42         false

maximum of 0 and 0 is 0   PASSED

如今咱們一眼能看出是max 方法失敗在輸入7與0。看主題上的更多細節在on Unrolled Method Names 這小節

Data Pipes

Data tables aren’t the only way to supply values to data variables. In fact, a data table is just syntactic sugar for one or more data pipes:

...

where:
a << [3, 7, 0]
b << [5, 0, 0]
c << [5, 7, 0]

數據表不知是提供數據變量的一種方式。實際上,一個數據表只是一個語法糖爲一個或者更多個數據管道。
一個數據管道,經過left-shift (<<) 操做符號,鏈接一個數據變量到一個數據提供者。數據提供者持有全部的值對變量,每次迭代之一。任何groovy已知對象如何遍歷被使用全部數據提供者。這個包含的對象有類型有 Collection, String, Iterable, and 實現了迭代器約定的對象。數據提供者不須要必須有數據。他們能獲取數據從外部數據源如文本文件,數據庫,電子表格,隨即生成數據。數據提供者被查詢
只在須要時獲取下一個值。

Multi-Variable Data Pipes

@Shared sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver")

若是一個數據提供者返回多個值在每一個迭代。它將同步鏈接多個數據變量。這個語法時相似groovy 多任務但使用元括號取代左側括號

def "maximum of two numbers"() {
    ...
    where:
    [a, b, c] << sql.rows("select a, b, c from maxdata")
}

不感興趣的數據值能被忽略使用一個下劃線
...

where:
[a, b, _, c] << sql.rows("select * from maxdata")

Data Variable Assignment

一個數據變量唄直接分配一個值
...

where:
a = 3
b = Math.random() * 100
c = a > b ? a : b

分配被從新評估在每一個迭代。如上面所示,右邊部分分配能夠關聯其餘數據變量。

...
where:
row << sql.rows("select * from maxdata")
// pick apart columns
a = row.a
b = row.b
c = row.c

Combining Data Tables, Data Pipes, and Variable Assignments

Data tables, data pipes, and variable assignments can be combined as needed:
數據表 數據管道 多個變量分配被組合做爲須要
...

where:
a | _
3 | _
7 | _
0 | _

b << [5, 0, 0]

c = a > b ? a : b

Number of Iterations

迭代間的數量依賴多少數據時可變的。連續執行相同的方法能產生不一樣數量的迭代。若是一個數據提供者運行出值比他的同行快,一個異常將產生。多個變量分配不能影響迭代數量。一個where:block只包含分配確切產生一個迭代。

Closing of Data Providers

After all iterations have completed, the zero-argument close method is called on all data providers that have such a method.
全部迭代完成後,沒有參數的關閉方法被調用在全部數據提供者有如此的方法

More on Unrolled Method Names

一分unrolled方法名根grooy的字符串相似。除了接下來的不一樣:
表達被標註# 代替 $,沒有相等於 ${…​}語法
表達式只支持屬性訪問和無參調用
給一個Person類只有name與age以及數據類型爲person的變量。接下來的校驗方法名是:

def "#person is #person.age years old"() { ... } // property access
def "#person.name.toUpperCase()"() { ... } // zero-arg method call

Non-string values (like #person above) are converted to Strings according to Groovy semantics.

The following are invalid method names:

def "#person.name.split(' ')[1]" { ... } // cannot have method arguments
def "#person.age / 2" { ... } // cannot use operators

If necessary, additional data variables can be introduced to hold more complex expression:

def "#lastName"() {
    ...
    where:
    person << ...
    lastName = person.name.split(' ')[1]
}
  1. 想法背後運行方法參數更好的被IDE支持。而後 最新版本的IntelliJ IDEA 自動認出數據變量,甚至從數據表的值推斷出他們的類型

  2. 例如,一個特性方法能使用數據變量在setup: block 但不能在其餘任何條件

  3. groovy語法不運行$符號在方法名稱中

相關文章
相關標籤/搜索