[.NET][BDD][TDD]BDD with SpecFlow by MS Test (1) – BDD與TDD範例

  • 13927
  • 0
  • 2012-11-01

[.NET][BDD][TDD]BDD with SpecFlow (1)–Install and UnitTestProvider by MS Test

前言

BDD 的全名為 Behavior-driven development ,在 2003 年由 Dan North 所命名,用來作為 TDD 的輔助。

在頗久之前,筆者就留意到了 BDD 這個東西,當時就是 TDD, DDD, BDD 這類的專有名詞不斷出現。之前覺得很酷,透過 DSL (Domain-specific language) 就可以用人話來輔助設計出程式與測試程式。但覺得離現實生活還是太遙遠了,連單元測試怎麼寫都是問題,TDD 都還不知道怎麼進行,直接跳到 behavior ,實在太艱難了。

隨著經驗的累積,單元測試、整合測試與重構,總算比較能夠上手並運用在實際的專案上,卻在 TDD 上,碰到了一點難題。TDD 不論技術面、概念面、流程面,我想都沒什麼太大問題,問題出在『測試案例』如何由 QA 的產出,變成測試程式的 input 。一來,沒 domain 還是不行,沒 domain 會導致只是 developer 在自 high 、自爽,寫出來的程式可能有完整的測試,功能也可正常運作,卻不是需求要的 feature 。 TDD 的 test cases ,淪為只是為了測試單元功能,或是用來測試較大或重要的行為。即使,過程是 TDD,概念是 TDD ,技巧是 TDD ,但卻還是少了 『模擬測試使用此物件的行為』。

在一個禮拜天早上,突然對 Agile 的 User Story 相當感興趣,不斷 survey 的過程中,突然想到了 BDD 這東西,不就是結合 user story 以及 TDD 的橋樑嗎?這個靈光一現,似乎找到了 TDD 的最後一塊拼圖。

所以,這一篇文章,就是我對 BDD 的初體驗,使用的工具是 SpecFlow ,一個 open source 的工具,License 為 BSD license。

 

安裝 SpecFlow

SpecFlow 的網站 Download 會連到 github,請見:Download Packages

image

我是使用 VS2010 ,所以是下載 1.8.1.msi ,如果要裝在 VS2012 的朋友,記得安裝 VS11beta_upd1.vsix 。

安裝過程,就都一直下一步就好,這邊為了節省篇幅,就不貼圖上來了。

註:如果沒有安裝 SpecFlow ,而直接在測試專案透過 NuGet 安裝,則無法在測試專案中,新增項目時,出現 SpecFlow 相關的 Template File 。

 

建立第一個 BDD 專案 with MS Test

官網的範例大部分都是使用 NUnit 來示範,甚至預設也是使用 NUnit 的設定,所以我就不對 NUnit 的設定多做介紹。這一篇文章要介紹,怎麼透過 MS Test 來進行 BDD 的測試。

當安裝完成後,請新建立一個『測試專案』,例如 TestAtmOperation:

image

 

建立 SpecFlow Feature File

如果您已經有安裝好 SepcFlow ,那麼在測試專案上,新增一個 item ,即可看到多出了三個項目類型: SpecFlow Event Definition, SpecFlow Feature File, SpecFlow Step Definition 。

這邊先選擇 SpecFlow Feature File ,並命名為 DrawMoney ,代表『提款』的 feature 。

image

接著在方案中就可以看到 DrawMoney.feature ,打開它之後,會看到預設的 template 內容:

image

接著,先新增放置 production code 的 Library,並在測試專案上,設定好參考。

image

接著給它勇敢的建置下去,碰!跳出 21 個錯誤,都是 DrawMoney.feature 找不到 NUnit 的錯誤。

image

因為預設的 UnitTest Provider 是 NUnit ,這邊我們希望改成用 MS Test 來跑 SpecFlow 的 BDD。

 

安裝 NuGet Package Manager

這邊要先請各位安裝一下 NuGet Manager,一切會快樂很多。

image

 

加入 SpecFlow 參考

裝好之後,在測試專案的參考上,滑鼠右鍵,選擇『管理NuGet套件』

image

接著搜尋『SpecFlow』。

image

給它安裝下去。

image

會看到參考中多了 TechTalk.SpecFlow ,而且多了 App.Config 檔案。

image

 

更改 SpecFlow 設定檔,改成 MS Test

打開App.Config,會看到 SpecFlow 的設定區塊已經加上去了, SpecFlow 很佛心地連 config 設定的網址都附上來了。

image

點 hyperlink 可以看到第一個區塊,就是跟我們說,怎麼將 unitTestProvider 設定成 MS Test 。當修改設定檔完成後, SpecFlow 會跳一個提示,問你是否要重新產生 feature 對應的測試程式,在這邊也就是我們的 DrawMoney.feature.cs 。按『是』就對了。

image

到這邊,基本上就設定完成了。

 

Run ! Test Run ! Test Run !

接下來跑一下測試,(我習慣的 hotkey 是 Ctrl+R, Ctrl+A),基本上我是拿執行單元測試來當作 build 用,以確保每次 build 程式碼除了符合編譯器規定,也能通過單元測試。

測試結果與相關訊息,如下圖所示:

image

測試結果出現了一個『結果不明』的狀態,這代表了 feature 檔,透過 DSL 所建立的 Unit Test 檔案內容中,相關的設定還沒初始化完成

而在錯誤訊息中,出現了很有趣的東西,看到了嗎?

第一行的 Assert.Inconclusive,No matching step definition found for one or more steps ,代表測試過程中,已經透過 feature 檔所產生的測試程式,來尋找有沒有對應的 step 檔案。且後續的訊息更揭露了, step 檔案中,應該要有的 function 內容。而且這些內容與 feature 檔案的 Scenario 是相吻合的。

接下來,就是要滿足測試程式所期望的測試結果。

 

建立 SpecFlow Step Definition

剛剛的測試結果提醒我們, feature 沒有對應的 step 定義,所以接下來就新增一個 Spec Flow Step Definition ,取名為 DrawMoneyStep 。

image

該檔案預設的 template 內容如下,該有的說明也都在註解中了:

image

已經滿足了原本的測試結果,再跑一下測試。測試的結果仍然是『結果不明』,但細部資訊已經不同了。

image

到這邊為止,其實就是 BDD 的起手式完成了。

 

說明

回顧一下預設 Feature 檔案的內容:

image

預設的 feature 檔案內容中, Feature 區段的意義:

  1. 定義了一個『加法器』,也就是 Feature: Addition。這邊可以把 Feature 當作 User Story 的名稱
  2. In order to avoid silly mistakes:代表這個 User Story 的價值,也就是 Business Value
  3. As a math idiot:代表了 User Role
  4. I want to be told the sum of two numbers:代表了目的,也就是 Goal

而 Scenario 的區段,就更直覺了:

  1. Scenario: Add two numbers。代表這是一個描述『將兩個數字相加的 Scenario 』。
  2. Given 關鍵字:可以當做環境設定,可以把它視為 3A 裡面的 Arrange
  3. And 關鍵字:則用來連接上一個子句,以這邊的例子來說,就是環境設定包括了:『I have entered 50 into the calculator And I have entered 70 into the calculator』。
  4. When 關鍵字:觸發的動作,可以把它視為 3A 裡面的 Act
  5. Then 關鍵字:代表應該出現什麼結果,可以把它視為 3A 裡面的 Assert

雖然把 Scenario 中的關鍵字與 3A 作結合,但不代表只有 Then 裡面,可以加上 Assert 。如果大家有用 DbC (Design by Contract) 的方式設計程式,就應該知道,在每一個步驟、環節,適當地透過 Assert 來檢查 Pre-Condition, Post-Condition,會讓程式碼更加健壯。

有了以上的概念,再來看預設的 Step 檔案內容,就不會太難懂了。

image

BDD 結束,接下來,我們來 TDD 吧!

備註:這邊先用預設的 Feature 與 Step 來設計『加法器』,請原諒我前面就把命名命成我下一篇要用的範例 >”<

 

TDD Start !!

第一步就先把原本的 Step 中內容刪掉,執行測試,將測試結果內容中的 Step 程式碼,放到 Step 中。

image

image

針對 Feature , 建立我們的測試目標:Calculator。並透過 Visual Studio 的產生功能,直接幫我們產生對應的 class 到 AtmOperation 的 project 中。

image

接著第一個 Given 為『輸入數字50到 calculator 』,這邊我們的設計,就是想辦法符合 Scenario 的 Step 就對了。所以針對 Calculator ,新增一個 FirstNumber 的屬性,並設定為50。

image

接下來,第二個 Given 為『輸入70到 calculator 』,這邊新增一個SecondNumber的屬性,並設定為70。

image

接下來,應該針對 When 來設計,[When(@"I press add")],當我按下 add 時,所以這邊為 Calculator 新增一個 Add 的方法。

image

最後,針對 Then 來驗證結果。 Scenario 上寫的很清楚,結果應該為 120 。所以這邊為 Calculator 新增一個 Result 的屬性,並驗證其值是否為 120 。

image

ok,我們針對 Calculator 加法的 Feature , 針對兩個數字的相加這個 Scenario ,已經寫完我們的測試程式了,執行一下測試吧!

碰!紅燈!放個煙火慶祝一下,我們已經進入 TDD 循環的第一個步驟:紅燈。寫一個一開始會執行失敗,但又符合測試目標物件行為的測試程式。

image

看一下詳細的測試結果資訊,可以發現兩個 Given 都是 done ,錯誤發生點是在 When I press add 。

image

接下來,神奇的地方要來了,我們直接在 feature 檔上, When 的部份加上中斷點。是的,你沒看錯,就是在 feature 檔案上新增中斷點。

接下來用偵錯測試的模式執行測試, When 的時候就會中斷:

image

按下 F11 ,可以進去 Step 的程式中:

image

再進去 Add 方法中, YES !! 總算看到我們的 production code 了,Add 方法中,還沒有實作相關的內容,所以測試會拋出 exception 。

image

接下來,我們要想辦法,滿足 Scenario 的需求,當 FirstNumber 為 50 ,而 SecondNumber 為 70 時, Result 結果應該是 120 。

所以 Add 的方法內容,要滿足這個需求,我們做了以下的設計:

image

看起來很 ok ,接下來再執行一次測試,可以看到測試結果是綠燈了!而且,在詳細的測試訊息中,可以在『標準主控台輸出』中,看到 Scenario 的執行結果,測試程式就像說故事一樣,表達了 Calculator 的一種行為。

image

到這邊,我們已經先使用 BDD 來描述物件行為,並自動產生 feature 中, Scenario 對應的測試程式。接下來透過 TDD 的方式,來執行測試與開發實際的程式。

或許,您會覺得這樣的程式,很沒有彈性,如果需要再新增第三個數字,第四個數字,該怎麼辦?您可以有兩個方式:

  1. 重構 Calculator ,但重構過程中,仍須滿足已經存在的測試程式。
  2. 等真的有該需求出現時,再來滿足需求後,重構程式。

對重構有興趣的朋友,可以參考一下:[ASP.NET]重構之路番外篇 –Refactoring to Patterns ,這邊就不再贅述。有興趣的朋友們,可以自行練習。

 

結論

原本練習 TDD 時,總覺得 test cases 不夠精準,無法呈現 domain 的行為,而 QA 或 PM 所設計的 test cases ,又無法與測試程式作結合。

BDD 把最艱困的一個阻礙點打通了,目標物件的行為,可以透過 Feature 來與 User Story 或 Use Case 結合,透過 Scenario 來產生測試程式的外框與繫結,可以讓測試程式就像在描述 Scenario 般一樣自然。

整個概念如下圖所示:

image

有了這樣的概念之後,下一篇文章,則拿一個生活上使用 ATM 的例子,來設計屬於我們自己的 feature 以及 scenario 。

 


blog 與課程更新內容,請前往新站位置:http://tdd.best/