cucumber java從入門到精通(2)用代碼定義步驟
上一節里我們定義了feature文件,feature文件就是自然語言描述的用例文件,它有一定的章法,具體的潛規則是:
- 使用Feature關鍵字定義定義功能名稱
- 使用Scenaio關鍵字定義定義測試場景名稱
- 使用Given關鍵字定義定義前置條件
- 使用When關鍵字定義定義測試步驟
- 使用Then關鍵字定義定義斷言
Feature文件是測試人員與客戶/產品經理進行需求交流的文檔工具,定義好Feature文件以后,我們的測試功能點實際上已經是定義完成了,下面的步驟很自然就是用代碼去實現這些測試點。當然這里要說明的是,我們用人肉手點這些測試功能點也是沒有太大問題的,但是想一想我們所處的開發階段,現階段我們只有整理成feature的原始需求,我們的系統還是一窮二白,一行代碼都沒有,因此用手點這個萬能利器我們是無法使用的了。 再來思考一下如果我們使用代碼去定義測試用例有什么好處?盡管系統目前沒有任何功能,但這並不能妨礙我們把自動化的測試用例給寫好(很大程度上,這是自動化的單元測試用例),有了用例,我們就有了標准。在后面的流程中只要開發人員編寫的代碼能夠跑通這些自動化用例,那么我們可以比較有信心的認為開發實現了客戶/產品經理所提出的需求。另外一點就是,自動化測試用例寫完是可以重復使用的,在日后的回歸測試、驗收測試和持續集成的過程中,這些用例將起到保障產品/代碼質量的關鍵作用。
使用cucumber定義用例(Step)
回到命令行,我們在cucumber_first\step_definitions
文件夾下新建1個名為TodoStep.java
的文件
type nul > step_definitions\TodoStep.java
用文本編輯器編輯該文件。在教程的開始階段我們並不需要使用Java IDE,因此你不需要糾結到底是使用eclipse還是intellij。在windows系統上,比較推薦的文本編輯器有Notepad++, sublime, gvim
。當然人各有所其所好,選擇任意一種都是可以的,不過因為sublime和gvim在設置文件編碼上面沒有Notepad++直觀,因此對於初學者來說使用Notepad++是一種比較穩妥的解決方案。
先把上一節中cucumber提示我們的那一大串代碼片段復制進TodoStep.java
,然后加入包名和依賴包的導入,你的TodoStep.java
文件應該是這樣的:
package step_definitions;
import cucumber.api.java.zh_cn.*;
import cucumber.api.PendingException;
import static org.junit.Assert.*;
public class TodoStep {
@假設("^我的任務清單里有(\\d+)個任務$")
public void 我的任務清單里有_個任務(int arg1) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@當("^我完成(\\d+)件任務之后$")
public void 我完成_件任務之后(int arg1) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@那么("^我還剩下(\\d+)件未完成的任務$")
public void 我還剩下_件未完成的任務(int arg1) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
}
看上去哪里有點奇怪。的確如此,我們的方法名是中文的,這不太符合一般的代碼審美。
重構方法名及參數名
是時候重構一下方法名和參數名了。這次我們重構的驅動力有下面2個:
- 方法名是英文會讓代碼看上去更整潔;
- 給方法的參數取一個更容易理解的名稱會讓代碼在日后更容易維護。
重構完成以后,TodoStep.java
中的代碼應該是這樣的:
package step_definitions;
import cucumber.api.java.zh_cn.*;
import cucumber.api.PendingException;
import static org.junit.Assert.*;
public class TodoStep {
@假設("^我的任務清單里有(\\d+)個任務$")
public void iHaveSomeTasks(int totalTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@當("^我完成(\\d+)件任務之后$")
public void iFinishSomeTasks(int finishedTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@那么("^我還剩下(\\d+)件未完成的任務$")
public void iLeftSomeTasks(int leftTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
}
重構完成之后我們發現我們的代碼目的現在一目了然,增加了適當的可維護性。
注意:穩妥起見,請將TodoStep.java
文件的編碼改為gb2312。
運行steps
運行我們剛定義的steps需要2個步驟
- 編譯
TodoStep.java
文件 - 運行feature文件的同時指定與feature文件相關的step文件所在的路徑
看起來有點繞,其實在命令行中不難完成
首先執行編譯命令:
# windows
javac -cp "./jars/*;." step_definitions\TodoStep.java
# linux or unix
# javac -cp "./jars/*:." step_definitions\TodoStep.java
注意到我們把當前路徑.
加到了CLASSPATH里,這個很容易漏掉。
然后運行feature文件
java -cp "./jars/*;." cucumber.api.cli.Main -g step_definitions features
-g
選項告訴cucumber我們要執行的steps文件存放在哪個文件夾里。同樣需要注意的是當前路徑.
必須放到CLASSPATH里。
如果沒有錯誤的話,你會看到下面的信息
java -cp "./jars/*;." cucumber.api.cli.Main -g step_definitions features
P--
1 Scenarios (1 pending)
3 Steps (2 skipped, 1 pending)
0m0.134s
cucumber.api.PendingException: TODO: implement me
at step_definitions.TodoStep.iHaveSomeTasks(TodoStep.java:11)
at ?.假設我的任務清單里有3個任務(todo.feature:6)
這個提示信息告訴我們,我們定義了1個Scenario和3個Steps,不過我們並沒有實現這些。因為我們的實現方法里只是簡單的拋出了1個Pending異常。
Pending異常很像是占位符,它告訴代碼的維護者這個測試步驟應該實現,只是現在還沒開工而已。
自動化漸進
每次都在命令行里敲一長串命令除了會讓你顯得技術高超手法嫻熟以外其實並不是什么特別有效率的事情(忽然想起來每次在命令行里用vim編輯文件各種前后台切換時總會有人發出驚嘆)。而且命令越長出錯的幾率越大。
為什么不讓這些命令自動執行呢?
先創建1個名為compile.bat
的文件,我們用批處理的方式來自動運行命令。編輯之:
type nul > compile.bat
# compile.bat
javac -cp "./jars/*;." step_definitions\TodoStep.java
再創建1個名為run.bat
的文件,輸入如下內容
type nul > run.bat
# run.bat
java -cp "./jars/*;." cucumber.api.cli.Main -p pretty -g step_definitions features
現在我們可以把繁冗的命令簡化成下面2個簡單的指令了
compile
run
如果你是在linux或unix機器上,你可以這樣做
touch compile
touch run
echo 'javac -cp "./jars/*:." step_definitions\TodoStep.java' > compile
echo 'java -cp "./jars/*:." cucumber.api.cli.Main -p pretty -g step_definitions features ' > run
chmod +x compile
chmod +x run
./compile
./run
總結
目前為止我們知道了2件事情
- cucumber先定義feature文件,該文件是用自然語言描述的
- 接下來要定義feature文件對應的step文件,這一步是用代碼實現的
下一節我們將實現我們的第1個cucumber step及斷言