cucumber java從入門到精通(4)Scenario Outline及數據驅動
到目前為止,我們的TodoList類工作良好,不過離我們的預期——任務清單系統還是有不少差距,究其原因不過如下:
- 我們的feature不太完畢,沒有測試任務清單的增刪改查完成等功能;
- 我們輸入的數據太過單一,只測試了1種輸入輸出的情況;
下面我們將着手解決數據輸入太過單一的問題。我們將使用Scenario Outline技術。
什么是Scenario Outline
什么是Scenario Outline呢,我們不妨先去命令行里看一下,在命令行中輸入
java -cp "jars/*" cucumber.api.cli.Main --i18n zh-CN
這將得到cucumber關鍵字的翻譯:
| feature | "功能" |
| background | "背景" |
| scenario | "場景", "劇本" |
| scenario_outline | "場景大綱", "劇本大綱" |
| examples | "例子" |
| given | "* ", "假如", "假設", "假定" |
| when | "* ", "當" |
| then | "* ", "那么" |
| and | "* ", "而且", "並且", "同時" |
| but | "* ", "但是" |
| given (code) | "假如", "假設", "假定" |
| when (code) | "當" |
| then (code) | "那么" |
| and (code) | "而且", "並且", "同時" |
| but (code) | "但是" |
我們可以看到scenario outline被翻譯成了場景大綱或者是劇本大綱。簡單來說,場景大綱可以理解為同一個場景同一部戲,內容都一樣,台詞也一樣,只是換了不同的演員來演。每個場景大綱定義了不同的演員列表,該場景的台詞是一樣,演員的動作也相同,只是每次換不同的演員去表演。所謂鐵打的營盤流水的兵。因此,場景大綱里定義了幾組演員,這個場景就要演幾次。
cucumber的場景大綱不是用來定義演員的,而是用來定義數據的。大綱里定義幾組數據,那么該場景就要跑幾次。
增加Example
現在我們給我們的feature增加1組測試數據:
#language: zh-CN
功能:任務管理
場景大綱: 完成任務
假設 我的任務清單里有<total>個任務
當 我完成<finished>件任務之后
那么 我還剩下<left>件未完成的任務
例子:
| total | finished | left |
| 3 | 1 | 2 |
| 5 | 1 | 4 |
運行一下
run
我們發現cucumber報錯了:
#language: zh-CN
功能: 任務管理
場景大綱: 完成任務 # todo.feature:5
假設我的任務清單里有<total>個任務
當我完成<finished>件任務之后
那么我還剩下<left>件未完成的任務
例子:
場景大綱: 完成任務 # todo.feature:12
假設我的任務清單里有3個任務 # TodoStep.iHaveSomeTasks(int)
當我完成1件任務之后 # TodoStep.iFinishSomeTasks(int)
那么我還剩下2件未完成的任務 # TodoStep.iLeftSomeTasks(int)
場景大綱: 完成任務 # todo.feature:13
假設我的任務清單里有5個任務 # TodoStep.iHaveSomeTasks(int)
java.lang.AssertionError: expected:<3> but was:<5>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:645)
at org.junit.Assert.assertEquals(Assert.java:631)
at step_definitions.TodoStep.iHaveSomeTasks(TodoStep.java:15)
at ?.假設我的任務清單里有5個任務(todo.feature:6)
當我完成1件任務之后 # TodoStep.iFinishSomeTasks(int)
那么我還剩下4件未完成的任務 # TodoStep.iLeftSomeTasks(int)
Failed scenarios:
todo.feature:13 # 場景大綱: 完成任務
2 Scenarios (1 failed, 1 passed)
6 Steps (1 failed, 2 skipped, 3 passed)
0m0.166s
java.lang.AssertionError: expected:<3> but was:<5>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:645)
at org.junit.Assert.assertEquals(Assert.java:631)
at step_definitions.TodoStep.iHaveSomeTasks(TodoStep.java:15)
at ?.假設我的任務清單里有5個任務(todo.feature:6)
看起來密密麻麻,實際上的意思就是場景大綱里的第二組數據報錯了,我們的TodoList的實現無法滿足第二組數據,想想這也是應該的。
在這里要解釋一下例子這個關鍵字。例子在英文里叫Example,是feature關鍵字。緊跟在例子后的一半都是1個數據列表,從上文可以看到,一目了然。數據列表有表頭,表后下面的行跟着的是測試數據。
Scenario Outline運行流程
Scenario Outline的運行流程就是先從例子里讀一行數據,然后根據該數據的column也即是表頭,在steps里找到相應的<column>字段,用具體的數據進行替換。例子里有2行數據這個Scenario就會運行2次。因此我們上面的feature文件實際上是這樣運行的:
假設 我的任務清單里有3個任務
當 我完成1件任務之后
那么 我還剩下2件未完成的任務
假設 我的任務清單里有5個任務
當 我完成1件任務之后
那么 我還剩下4件未完成的任務
重構並讓用例通過
我們的用例已經測試出TodoList類的缺陷了,是時候重構一下了,先重構TodoStep.java文件
// TodoStep.java
package step_definitions;
import cucumber.api.java.zh_cn.*;
import cucumber.api.PendingException;
import static org.junit.Assert.*;
import implementation.TodoList;
public class TodoStep {
TodoList todo;
@假設("^我的任務清單里有(\\d+)個任務$")
public void iHaveSomeTasks(int totalTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
todo = new TodoList();
todo.setTotalTaskCount(totalTasks);
}
@當("^我完成(\\d+)件任務之后$")
public void iFinishSomeTasks(int finishedTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
todo.finishTask(finishedTasks);
}
@那么("^我還剩下(\\d+)件未完成的任務$")
public void iLeftSomeTasks(int leftTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
assertEquals(todo.getRestTasksCount(), leftTasks);
}
}
我們這里假設TodoList有1個setTotalTaskCount方法,該方法用來設置當前TodoList中task的總數。另外我們還把假設中的斷言給去掉了,這是因為如果假設中有斷言,那就意味着你連前置條件都不信任,既然前提條件都不能保證,那么下面的步驟就沒有太多意義了,所以去掉會讓步驟定義更加的符合邏輯。
再重構TodoList.java文件
package implementation;
public class TodoList {
int totalTaskCount;
int finishedTaskCount;
public TodoList() {
totalTaskCount = finishedTaskCount = 0;
}
public int getTotalTaskCount() {
return totalTaskCount;
}
public void setTotalTaskCount(int count) {
totalTaskCount = count;
}
public void finishTask(int count) {
finishedTaskCount = count;
}
public int getRestTasksCount() {
return totalTaskCount - finishedTaskCount;
}
}
代碼很簡單,就不一一解釋了。
運行一下
compile && run
結果如下,所有的step都pass了。
#language: zh-CN
功能: 任務管理
場景大綱: 完成任務 # todo.feature:5
假設我的任務清單里有<total>個任務
當我完成<finished>件任務之后
那么我還剩下<left>件未完成的任務
例子:
場景大綱: 完成任務 # todo.feature:12
假設我的任務清單里有3個任務 # TodoStep.iHaveSomeTasks(int)
當我完成1件任務之后 # TodoStep.iFinishSomeTasks(int)
那么我還剩下2件未完成的任務 # TodoStep.iLeftSomeTasks(int)
場景大綱: 完成任務 # todo.feature:13
假設我的任務清單里有5個任務 # TodoStep.iHaveSomeTasks(int)
當我完成1件任務之后 # TodoStep.iFinishSomeTasks(int)
那么我還剩下4件未完成的任務 # TodoStep.iLeftSomeTasks(int)
2 Scenarios (2 passed)
6 Steps (6 passed)
0m0.133s
總結
從上面的例子里我們就可以看出自動化對生產力的提升幫助巨大。假設我們還需要測試100組數據,如果人肉手點的話,那么執行用例的人自然是痛不欲生,而且我們也沒有辦法完全保證數據輸入的准確性,畢竟老虎也會有打盹的時候,何況是人。但是如果用自動化測試的話,增加數據無非就是在數據表中增加一些行,工作量不是特別大,而且可以比較容易的檢查出數據是否准確。
像這種以增加輸入輸出數據的方式增加用例的測試用例設計方法,我們可以稱之為數據驅動。
下一節我們將進一步的實現項目工程學上的自動化,我們將使用maven來搭建cucumber項目。
