[Tool][Specflow]使用 Scope 標記 Step Binding
前言
相信使用過 Specflow 來設計 feature 與 steps 的朋友,應該都有碰到一個尷尬的問題,就是 Steps 是全域的。因為 Scenario 上的 Step, 如 Given, When, Then 子句,如何與 Steps.cs 來對應,是透過對應的 attribute 來 binding ,如下圖所示:
這個 Scenario 上面的 When ,實際執行時是要執行哪一個方法,當我們從 When 呼叫計算運費
移至定義可以看到,其實 Step 與 Feature 是透過 [When(@"呼叫計算運費")]
來 binding ,如下圖所示:
然而當有多個 Feature 或 Scenario 擁有相同的 Step 描述時,Specflow 會很聰明地幫你指到同一個 Step 的方法上,讓開發人員可以重用 Scenario 的 Step 。這在同一個 Feature 檔中,即使是不同的 Scenario ,仍算是很 friendly 的功能,但是一旦跨 Feature ,很容易造成維護或追問題的困擾。例如,有三個貨運商要計算商品的運費,其 Feature 如下:
即使參數不同,但這些 Step 的描述都是相同的,因此預設都會使用同一個 function 。但問題來了,黑貓的計算運費,可能是要呼叫 BlackCat 的 CalculateFee() ,但郵局的計算運費,可能是要呼叫 PostOffice 的 CalculateFee() 。很明顯的,雖然描述都是計算運費,但實際上要測試的物件是不同的。
因此,雖然 Step 預設可以重用,但我們還是相當需要簡單的方式,讓我們可以建立專屬於某個 Feature 或 Scenario 的 Step 內容。
Scope Introduction
在 Specflow 中其實有提供一個 attribute: Scope
來滿足我們上述的需求。Scope attribute 如下圖所示:
可以發現這個 attribute 可以放在 Class 上,也可以放在 Method 上。放在 class 上代表,整個 Step 的 class 都是給這個 Scope 所定義的範圍使用。同樣地,放在 method 上就代表這個 step 方法內容,是專屬於某個 Feature, Scenario 或是 Tag 使用。
另外一個值得留意的是:AllowMultiple = true
。代表同樣的 attribute 可以重複標記在同一個 class 或 method 上。
來看一下例子,針對上面的 3 個 Feature 上,在 When 呼叫計算運費
上移至定義,都會移到「黑貓Steps 」上的方法。如下圖所示:
Scope with Feature
當我們一旦在 When 的方法上,加上 [Scope(Feature = "黑貓")]
代表這個 function 只給「Feature 黑貓」使用。
此時再回到郵局與新竹貨運的 Feature 上,可以發現這兩個 Feature 上 Scenario 的 「When 呼叫計算運費 」已經找不到對應的 binding 了。如下圖所示:
Scope with Multiple Feature
倘若這個呼叫計算運費的方法,也要給郵局這個 Feature 使用,只需要透過 multiple attribute 的標記方式即可,如下圖所示:
黑貓與郵局就都可以 binding 到上面這個 step ,如下所示:
Scope with Scenario
除了 Feature 等級, Scope 也可以納入特定的 Scenario 名稱,例如我們先將新竹貨運的 scenario 多增加幾個,如下所示:
可以看到When 呼叫計算運費
都沒有 binding 到對應的 steps。
這時在 step 加上 [Scope(Scenario = "新竹貨運計算運費")]
,這樣就代表 Scenario 名稱為「新竹貨運計算運費」中的When 呼叫計算運費
,就會跟這個方法 binding 起來。如下所示:
Scope with Tag
除了 Feature 與 Scenario 外,還有一個比較彈性的標記範圍方式: Tag 。如上圖所示,我們針對一個 Scenario 上,新增了一個 tag 名稱為 JoeyTag 。接著一樣在 Steps 方法上,加入 [Scope(Tag = "JoeyTag")]
,如下所示:
再檢視新竹貨運的 Scenarios ,可以看到有標記 @JoeyTag
的 Step 也可以被 binding 到了。如下所示:
個人建議
我個人的習慣,通常是考量團隊對測試的熟悉程度,我會讓 Feature 與 Steps 檔案做一對一的對應,也就是一個「黑貓 Feature」對應一個「黑貓 Steps」。所以我習慣在 Steps 的 class 上直接標記 [Scope(Feature="黑貓")]
,這樣同一個 Feature 中不同的 Scenario 仍可以共用 Steps ,又可以避免跨 Feature 的問題。如下所示:
如果有跨 Feature 共用的需求,尤其是 Event Binding 的需求,例如 BeforeScenario, BeforeFeature 之類的 event hook ,那建議直接新增一個 Hook 的 class 來處理,讓團隊在開發時有個 rule 可以 follow, 也讓維護的人更了解要去哪找到對應的程式碼。如下所示:
結論
因為 Specflow 的文件算是少的,希望之前跌跌撞撞摸索出來的一些小技巧,對想要了解的讀者有所幫助,可以更快樂地使用 BDD 與 Specflow 進行開發。
記得,善用 Scope ,定義出團隊規範,可以節省時間跟避免很多問題。
blog 與課程更新內容,請前往新站位置:http://tdd.best/