cucumber java從入門到精通(3)簡單實現及斷言


cucumber java從入門到精通(3)簡單實現及斷言

上一節里我們定義了step的java代碼實現文件,step就是測試步驟及斷言的集合,我們先定義出來,以后可以驅動開發以及在持續集成時重用。

這一節我們將近距離細觀一下所謂的step java實現。以下面的代碼片段為例:

public class TodoStep { //1                                                                             
    @假設("^我的任務清單里有(\\d+)個任務$") //2                                                    
    public void iHaveSomeTasks(int totalTasks) throws Throwable { //3                        
        // Write code here that turns the phrase above into concrete actions     
        throw new PendingException();                                //4            
    }                                                                            
}
  • //1 定義了public class,這沒什么好說的;
  • //2 假設注解,這個注解表明下面的方法對應的也就是feature文件中我的任務清單里有xxxx個任務這個步驟;
  • //3 定義了具體實現feature文件步驟的方法,並從feature定義中取得傳入參數,也就是xxxx個任務的具體值;
  • //4 拋出Pending異常,表明該步驟暫未實現,但來日方長,也許有天可以實現;

cucmber執行順序

如果你對上面的代碼尚有疑問,那么是時候看一下cucumber的執行順序了,以下面代碼片段為例:

# feature
假設 我的任務清單里有3個任務 // 1
當 我完成1件任務之后 // 2
那么 我還剩下2件未完成的任務 //3

# step
@假設("^我的任務清單里有(\\d+)個任務$") //4                                                   
    public void iHaveSomeTasks(int totalTasks) throws Throwable { //5                       
        // Write code here that turns the phrase above into concrete actions     
        throw new PendingException();                                            
    }                                                                            
                                                                                 
@當("^我完成(\\d+)件任務之后$") //6                 
public void iFinishSomeTasks(int finishedTasks) throws Throwable { //7                        
    // Write code here that turns the phrase above into concrete actions  
    throw new PendingException();                                            
}                                                                            
                                                                             
@那么("^我還剩下(\\d+)件未完成的任務$") //8                                                 
public void iLeftSomeTasks(int leftTasks) throws Throwable { //9                        
    // Write code here that turns the phrase above into concrete actions        
    throw new PendingException();                                            
}                                    
  • 當我們運行了run命令后(還記得上一節的run嗎?其實就是執行了cucumber.api.cli.Main。),cucumber會去找feature文件,然后執行第1條feature語句,也就是//1
  • 執行//1的時候,cucumber會去找對應的step定義文件,尋尋覓覓的過程中,cucumber發現了對應的注解,也就是//4
  • //4告訴cucumber,下面緊接着定義的那個方法就是該feature對應的代碼實現,於是cucumber再去執行//5
  • 在執行//5的時候,cucumber將3作為totalTasks這個參數傳給//5
  • cucumber執行//5方法體中的內容並收到pending異常,於是該feature執行結束,轉去執行下1條feature

小練習:你能自己說明//2以及//3的執行順序嗎

漸進重構之假裝實現

回憶一下我們的測試目標,我們要測試一個todo list,第1步也就是在測試背景或者叫做前置條件里,我們是這樣描述的

假設 我的任務清單里有3個任務 // 1

這時候不妨假設我們有個TodoList類,該類有個getTotalTaskCount()方法返回任務清單中一共有多少條任務。基於這個想法,我們重構一下TodoStep.java文件


@假設("^我的任務清單里有(\\d+)個任務$")                                                   
    public void iHaveSomeTasks(int totalTasks) throws Throwable {                        
        // totalTasks == 3
        TodoList todo = new TodoList();
        assertEquals(todo.getTotalTaskCount(), totalTasks);
    }   

在這里要說明的是注解中的(\d)表示獲取feature定義中的數字字符並把該數字(int)作為totalTasks參數傳入iHaveSomeTasks方法。因此totalTaks應該等於3。另外assertEquals是jUnit的斷言方法

執行一下compile && run,得到下面的結果

step_definitions\TodoStep.java:11: 錯誤: 找不到符號
                TodoList todo = new TodoList();
                ^
  符號:   類 TodoList
  位置: 類 TodoStep
step_definitions\TodoStep.java:11: 錯誤: 找不到符號
                TodoList todo = new TodoList();
                                    ^
  符號:   類 TodoList
  位置: 類 TodoStep
2 個錯誤

我們無法編譯,因為TodoList這個類並沒有定義。

下面我們定義一下TodoList類。在implementation文件夾中新建1個名為TodoList.java的文件,該文件的實現是

// TodoList.java
package implementation;

public class TodoList {
    public int getTotalTaskCount() {
        return 3;
    }
}

下面在TodoStep.java中導入TodoList類

// TodoStep.java
import implementation.TodoList;

修改一下compile文件,因為這次我們需要TodoList.java文件,修改完成的版本應該是這樣的

# compile
javac -cp "./jars/*;." step_definitions\TodoStep.java implementation\TodoList.java
# linux && unix
javac -cp "./jars/*:." step_definitions\TodoStep.java implementation\TodoList.java

然后運行compile && run,得到下面的結果

#language: zh-CN
功能: 任務管理

  場景: 完成1件任務       # todo.feature:5
    假設我的任務清單里有3個任務 # TodoStep.iHaveSomeTasks(int)
    當我完成1件任務之后     # TodoStep.iFinishSomeTasks(int)
      cucumber.api.PendingException: TODO: implement me
        at step_definitions.TodoStep.iFinishSomeTasks(TodoStep.java:19)
        at ?.當我完成1件任務之后(todo.feature:7)

    那么我還剩下2件未完成的任務 # TodoStep.iLeftSomeTasks(int)

1 Scenarios (1 pending)
3 Steps (1 skipped, 1 pending, 1 passed)
0m0.128s

cucumber.api.PendingException: TODO: implement me
        at step_definitions.TodoStep.iFinishSomeTasks(TodoStep.java:19)
        at ?.當我完成1件任務之后(todo.feature:7)

我們看到有1個step pass了,那就是我們剛定義前置條件step,所以到現在為止我們干的不錯!我們有了feature文件,該文件描述了需求和測試用例,我們完成了測試step中的前置條件,並且我們實現了1個真正的TodoList類,盡管這個類目前還是嗷嗷待哺,不過當我們實現更多的step之后,TodoList類的功能會進一步完善,直到滿足用戶需求。實際上我們現在做的就是BDD,用測試去驅動開發。

可能有的同學會對此不屑一顧,TodoList類到目前為止只是自欺欺人的實現了1個返回int型數字3的方法,離我們所要的任務清單還相差十萬八千里。其實不用擔心這個,我們的TodoList類沒有實現具體的功能是因為我們實現的測試步驟還不夠多,我們接收到的需求還只是那么一點點,當我們實現更多步驟之后,TodoList自然會羽翼豐滿。這就是所謂的最小實現原則。

漸進重構,重構剩下的2個步驟

下面我們重構剩下的2個步驟。

我們先假設TodoList有1個finishTask方法,每次調用這個方法就會完成n個任務,n從參數傳入。於是剩余的任務就會減去n。
我們再假設TodoList有1個getRestTasksCount方法,調用這個方法可以獲取剩下的task的數量。
重構完成以后,我們的代碼如下所示

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();
        assertEquals(todo.getTotalTaskCount(), totalTasks);
    }                                                                            
                                                                                 
    @當("^我完成(\\d+)件任務之后$")                                                       
    public void iFinishSomeTasks(int finishedTasks) throws Throwable {                           
        // Write code here that turns the phrase above into concrete actions     
        todo.finisheTask(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.java文件。

我們先實現finishedTask方法,這個方法的方法體是空,表示什么都不做;
然后實現getRestTasksCount方法,這個方法簡單的返回2就好;

#TodoList.java
package implementation;

public class TodoList {
    public int getTotalTaskCount() {
        return 3;
    }
    
    public void finishTask(int count) {
        
    }
    
    public int getRestTasksCount() {
        return 2;
    }
}

再次運行compile && run

可以看到,我們的step全部運行通過了

#language: zh-CN                                                      
功能: 任務管理                                                              
                                                                      
  場景: 完成1件任務       # todo.feature:5                                   
    假設我的任務清單里有3個任務 # TodoStep.iHaveSomeTasks(int)                     
    當我完成1件任務之后     # TodoStep.iFinishSomeTasks(int)                   
    那么我還剩下2件未完成的任務 # TodoStep.iLeftSomeTasks(int)                     
                                                                      
1 Scenarios (1 passed)                                                
3 Steps (3 passed)                                                    
0m0.132s                                                              

總結

從這一節可以看到,我們的cucumber BDD行為驅動開發的流程是這樣的:

  1. Describe behaviour in plain text
  2. Write a step definition in Java
  3. Run and watch it fail
  4. Write code to make the step pass
  5. Run again and see the step pass
  6. Repeat 2-5 until green like a cuke

小練習:翻譯上面的6句話,弄清楚具體的意思。對照教程,請指出每一步與教程中所對應的代碼

我們簡單實現了TodoList類,目前的TodoList類是滿足我們的feature的需求的。下一節我們將增加更多的測試數據,以便完善TodoList類。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM