l

2023年3月22日 星期三

使用ezSpec落實行為驅動開發與實例化需求(5):撰寫Scenario Outline的四種方法

March 21 09:47~12:28

▲圖1:Scenario Outline範例

 

前言

前兩集介紹在ezSpec中撰寫Scenario的方式(請參考<使用ezSpec落實行為驅動開發與實例化需求(3):撰寫Scenario與傳遞簡單參數> 與 <使用ezSpec落實行為驅動開發與實例化需求(4):在Scenario中使用表格>)。當你開始採用BDD(行為驅動開發)或SBE(實例化需求)開發系統,針對同一個Feature或Story中的多個Scenario,很可能發生執行步驟相同或相似,只有輸入的資料以及在When中所用來檢查的expected result不同的情況。此時可考慮將這些Scenario改寫成Scenario Outline,用以簡化feature file的撰寫。

ezSpec支援四種不同的方式撰寫與執行Scenario Outline,以下逐一介紹。

***

方法一:用For Each執行Scenario Outline

Scenario翻譯成劇情場景,Scenario Outline則是劇情大綱場景大綱。簡單來說,Scenario Outline就是餵一組資料給Scenario,讓這個Scenario執行好幾次。用傳統測試的術語,這種測試稱為參數化測試案例資料驅動測試案例

圖1是一個簡單的Scenario Outline,其目的為依據商品未稅金額計算含稅總價,一共有四個例子(四筆測試資料),分別涵蓋四捨五入、可開立零元發票以及兩個邊界條件。為了撰寫這四個例子,最無腦的方式是把它們寫成四個Scenario。但是這樣一來這四個Scenario除了測試資料不同以外,其他的執行步驟都一樣,也就是說寫成四個Scenario會產生很多重複程式碼,此時改用Scenario Outline就可以解決這個問題。

為了儘量與JUnit 5整合,ezSpec提供好幾種撰寫Scenario Outline的方式。首先看到圖2:

  • 第74行:執行 newScenarioOutline() 在Story身上產生一個新的Scenario Outline。
  • 第75行:用 WithExamples(examples) 將指定該Scenario Outline所使用的Examples(也就是測試資料)。
  • 第76行:呼叫 RuntimeScenarios() 獲得List<RunimeScenario>。Scenario Outline會依據所輸入的Examples資料筆數,產生N個RunimeScenario。
  • 第77行:使用 forEach(example -> {} ) 的方式來指定每一次執行RunimeScenario的Step。

之後的寫法就跟一般的Scenario一樣,唯一的差別在於讀取Examples參數的方式。在標準的Scenario中,透過env.row() 可以讀取表格中的資料。在Scenario Outline的情況下,RunimeScenario每次只會看到一筆Examples的資料,此時要用env.getInput().get(column_name) 的方式來讀取Examples的資料,如圖2的82~85行所示。

 

▲圖2:ezSpec的Scenario Outline範例,採用forEach執行

 

由於圖2的Scenario Outline只是一般標準的test method,因此在JUnit的報表中,只會看到一筆執行結果,如圖3。

 

▲圖3:圖2執行結果的JUnit報表

 

雖然JUnit報表中只會有一筆資料,但ezSpec產生的報表會包含Scenario Outline每次的執行結果,請參考圖4。


▲圖4:圖2執行結果的ezSpect報表

***

方法二:用@ParameterizedTest執行Scenario Outline

如果開發人員希望在JUnit的報表中看到Scenario Outline每一次執行的結果,可以改用JUnit 5的@ParameterizedTest來執行Scenario Outline,請參考圖5。JUnit 5的ParameterizedTest直接將每次執行的測試資料透過test method的arguments接收資料,ParameterizedTest支援多種指定測試資料(相當於圖1的Examples)的方法,圖5所示為透過@CsvSource的方式指定測試資料。

使用ezSpec透過ParameterizedTest撰寫Scenario Outline的方式與圖2類似,除了將原本的@Test換成@ParameterizedTest,以及使用@ParameterizedTest所規定的設定測試資料方式以外,主要有以下兩點不同之處:

  • 不需要自行使用for each:因為JUnit 5的ParameterizedTest會自動執行該test method好幾次,因此開發人員就不需要如同圖2中透過RuntimeScenarios().forEach的方式來執行RuntimeScenarios。
  • 用WithParameterizedExamples取代圖2的WithExamples:由於ParameterizedTest的測試資料不包含表格的表頭,因此需要透過WithParameterizedExamples將測試資料的表頭告知ezSpec,如此一來在描述每一個Step的時候,若使用到 <tax_excluded> 等變數時,ezSpec才知道要如何將這些變數替換成測試資料中實際的數值。

 

▲圖5:使用@ParameterizedTest執行ezSpec的Scenario Outline

 

使用ParameterizedTest有一個好處,就是JUnit的報表可以顯示每次Scenario Outline的執行結果,如圖6所示。


▲圖6:圖5執行結果的JUnit報表

 

***

方法三:用@EzScenarioOutline執行Scenario Outline

開發人員也可以使用ezSpec的@EzScenarioOutline來執行Scenario Outline,如圖7所示。在這種情況下就不需要使用RuntimeScenarios().forEach的方式來執行RuntimeScenarios,ScenarioOutline的內容除了要呼叫WithExamples以外,基本上與Scenario沒什麼差別。主要差異有以下三點:

  • @EzScenarioOutline要自行執行執行次數,以圖7的例子,因為測試資料有四筆,因此指定@EzScenarioOutline(4)
  • test method需要接受一個型別為RepetitionInfo的參數,如圖7第119行。
  • 要改用Execute(repetitionInfo.getCurrentRepetition())來執行RuntimeScenarios,如圖7第138行。

 

▲圖7:使用@ParameterizedTest執行ezSpec的Scenario Outline

 

 

使用@EzScenarioOutline可以達到和@ParameterizedTest類似的效果,在JUnit的報表中顯示每次Scenario Outline的執行結果(但無法顯示文字說明),如圖8所示。


▲圖8:圖7執行結果的JUnit報表

***

方法四:用@DynamicScenario執行Scenario Outline

最後一種執行Scenario Outline的方式,使是用ezSpec的@DynamicScenario,請參考圖9。@DynamicScenario的撰寫方式和@EzScenarioOutline大致相同,不同點有:

  • test method須回傳DynamicNode,如圖9第141行。
  • DynamicExecute()取代Execute(),如圖9第160行。
  • return整個DynamicExecute執行結果,如圖9第149行。

 

▲圖9:使用@DynamicScenario執行ezSpec的Scenario Outline


使用@DynamicScenario在JUnit的報表中可以顯示最詳細的資料,除了每次Scenario Outline的執行結果,還包含每一個Step的內容與執行結果,如圖10所示。

 

▲圖10:圖9執行結果的JUnit報表

***

結語

本集介紹四種不同撰寫與執行Scenario Outline的方法,除了撰寫方式略有不同,以及JUnit產生的報表有所差異以外,其執行結果ezSpec所產生的報表基本上都是相同的。開發人員可以選擇自己認為比較喜歡的與法表達方式來撰寫Scenario Outline。

在本集中Scenario Outline的Examples是一個簡單的表格,下一集將介紹如何在Scenario Outline中指定表格中含有表格的測試資料。

***

友藏內心獨白:太多選擇也是一種煩惱。

沒有留言:

張貼留言