前兩篇文章介紹到了,為什麼需要BDD來輔助我們進行TDD,原因是需求、user story與acceptance test cases都是透過DSL來描述,這離最後要implement的程式碼還太遙遠,導致無法從驗收測試案例的描述,簡單的轉換到TDD的過程,這中間的gap太大。
上篇文章介紹的BDD工具是SpecFlow,也稍微介紹了如何安裝,以及其feature檔中feature與scenario描述的相關注意事項,及其與user story/acceptance test cases的對照。更帶到了如何透過scenario來定義清楚acceptance test cases,再從scenario定義出對應到Unit Testing的3A原則。
接下來這篇,就承接著上篇文章,用預設的feature與scenario,透過TDD的方式來完成一個加法器。
上一篇文章:[Day 24]BDD - SpecFlow Introduction
本系列文章專區
@TDD Start
SpecFlow 1.9.0 之前版本
第一步就先把原本的 Step 中內容刪掉,執行測試,將測試結果內容中的 Step 程式碼,放到 Step 中。執行時的錯誤訊息,如下圖所示:
將錯誤訊息中的提示,貼到Step檔後,如下圖所示:
上述這是SpecFlow 1.8版的建議方式,但1.9.0以後的版本,不用這麼麻煩了,接下來介紹1.9.0以後的版本,怎麼樣可以更快產生對應的step檔,而不需要先執行錯誤,再自行複製貼上。
SpecFlow 1.9.0 以後的版本
打開Feature檔,可以看到當Scenario中,若無對應的step描述,則會顯示出不同顏色,如下圖紫色的區塊:
若feature上仍有尚未定義的step,則點滑鼠右鍵,menu中就有「Generate Step Definition」的選項。
點選後Specflow會自動判斷,該feature檔上還有哪些尚未定義的step,就像VS2010自動產生單元測試案例一般的畫面,如下圖所示:
上面有兩個選項:
產生完Step檔之後,scenario的部份就會變成白色的了,如下圖所示:
這個檔案就會跟之前的版本一模模一樣樣。
@TDD的第一步
了解feature與scenario之後,我們的目標物件為Calculator。
針對 Feature , 建立我們的測試目標:Calculator。並透過 Visual Studio 的產生功能,直接幫我們產生對應的 class 到 AtmOperation 的 project 中。如下圖所示:
接著第一個 Given 為『輸入數字50到 calculator 』,這邊我們的設計,就是想辦法符合 Scenario 的 Step 就對了。所以針對 Calculator ,新增一個 FirstNumber 的屬性,並設定為50。如下圖所示:
接下來,第二個 Given 為『輸入70到 calculator 』,這邊新增一個SecondNumber的屬性,並設定為70。如下圖所示:
接下來,應該針對 When 來設計,[When(@"I press add")],當我按下 add 時,所以這邊為 Calculator 新增一個 Add 的方法。如下圖所示:
最後,針對 Then 來驗證結果。 Scenario 上寫的很清楚,結果應該為 120 。所以這邊為 Calculator 新增一個 Result 的屬性,並驗證其值是否為 120 。如下圖所示:
到這,我們幾乎只是一直在用Visual Studio內建的產生類別、屬性跟方法的功能,一點都不難吧。類別、屬性與方法的命名,也會完全符合scenario的情境與意義。
ok,我們針對 Calculator 加法的 Feature , 針對兩個數字的相加這個 Scenario ,已經寫完我們的測試程式了,執行一下測試吧!
碰!紅燈!放個煙火慶祝一下,我們已經進入 TDD 循環的第一個步驟:紅燈。寫一個一開始會執行失敗,但又符合測試目標物件行為的測試程式。
看一下詳細的測試結果資訊,可以發現兩個 Given 都是 done ,錯誤發生點是在 When I press add 。如下圖所示:
@在Scenario上偵錯
接下來,神奇的地方要來了,我們直接在 feature 檔上,scenario中的 When 部份加上中斷點。是的,你沒看錯,就是在 feature 檔案上新增中斷點。
接下來用偵錯測試的模式執行測試, When 的時候就會中斷,如下圖所示:
按下 F11 ,可以進去 Step 的程式中,如下圖所示:
再進去 Add 方法中, YES !! 總算看到我們的 production code 了,Add 方法中,還沒有實作相關的內容,所以測試會拋出 exception 。如下圖所示:
接下來,我們要想辦法,滿足 Scenario 的需求,當 FirstNumber 為 50 ,而 SecondNumber 為 70 時, Result 結果應該是 120 。
所以 Add 的方法內容,要滿足這個需求,我們做了以下的設計:
看起來很 ok ,接下來再執行一次測試,可以看到測試結果是綠燈了!而且,在詳細的測試訊息中,可以在『標準主控台輸出』中,看到 Scenario 的執行結果,測試程式就像說故事一樣,表達了 Calculator 的一種行為。如下圖所示:
到這邊,我們已經先使用 BDD 來描述物件行為,並自動產生 feature 中, Scenario 對應的測試程式。接下來透過 TDD 的方式,來執行測試與開發實際的程式。
@小結
或許,您會覺得目前這樣的程式,很沒有彈性,如果需要再新增第三個數字,第四個數字,該怎麼辦?您可以有兩個方式:
重構的部份,相信讀者朋友們經過前面一系列的洗禮,應該可以駕輕就熟了。如果想複習一下的朋友,請參考前面 Day9~Day19 的文章。
筆者的心得是:
原本練習 TDD 時,總覺得 test cases 不夠精準,無法呈現 domain 的行為,而 QA 或 PM 所設計的 test cases ,又無法與測試程式作結合。
BDD 把最艱困的一個阻礙點打通了,目標物件的行為,可以透過 Feature 來與 User Story 或 Use Case 結合,透過 Scenario 來產生測試程式的外框與繫結,可以讓測試程式就像在描述 Scenario 般一樣自然。
整個概念有點類似下圖所示:
註:嚴格來說,上圖應該加上ATDD的stage,會更為清楚。不過這邊僅當示意簡圖。
下一篇文章,我們會為BDD做個總結,讓讀者朋友可更清楚的了解BDD在整個開發流程中的定位。
我在 Generate Step Definition 時,的確有看到四個 method
但是 Generate 出來的 .cs 只有三個,好像有兩個被合併了!
I have entered 50 into the calculator 和 Given I have entered 70 into the
calculator 合併成 I have entered (.*) into the calculator
<pre class="c" name="code">
[Given(@"I have entered (.*) into the calculator")]
public void GivenIHaveEnteredIntoTheCalculator(int p0) {
ScenarioContext.Current.Pending();
}
這時候是要手動複製一個嗎?還是可以有其他的做法呢?
我問題怎麼這麼多啊
不用手動複製啊,在SpecFlow裡面,預設會幫你用regular expression,把相同scenario的,透過參數來取代。(這是為了避免同樣的function重複產生)
如果你真的不想透過regular expression來合併參數,那再generate那邊有個checkbox,取消就好了。
原來如此~~~我太粗心了
Geberate 旁邊沒有 checkbox
hatelove提到:
看一下詳細的測試結果資訊,可以發現兩個 Given 都是 done
可是我的不是 done, 而是出現 binding error....
針對scenario偵錯一下吧 :)
原來會出現 binding error 是因為我手動複製(複製 GivenIHaveEnteredIntoTheCalculator)的結果,雖然不能兩個,我後來是不手動複製,只有一個而已~就可以做出一樣的結果了(done)
hatelove提到:
按下 F11 ,可以進去 Step 的程式中,如下圖所示
又一個問題 (sorry, 我好煩喔~~)
在 Visual studio 2012 中,UnitTestProject 如果直接按F11他會出現以下訊息
A project with an Output Type of Class Library cannot be started directly. In order to debug this project, add an executable....(等等)
之前是可以在 test case 中下中斷點,然後 TEST -> Run -> All tests 來執行,並且中斷點也有效。可是這個方式在這裡 Scenario 好像不管用 ,是不是有什麼地方我忽略掉了呢?
感謝加菲貓大大~~~
可以了XD
好神奇阿~我先在 test case 下斷點,之後他就停在我先前下的斷點~
接著我再把 test case 斷點取消,他還是可以停在 scenario 這個斷點~~
好神奇阿~終於又跨了一步了
原來是要用 Ctrl + D, A 他才會停~~~
以前都沒注意到這點~~~~~
VS2012 跑SpecFlow 1.9.0跟1.9.1 應該有bug唷。
specflow的forum上有被提出來,也有被fix了,但我印象中還沒有release fix完成的版本出來。所以建議您如果碰到問題,就還是先用VS2010會比較好一些 Orz
要偵錯Scenario,只需要在Scenario上的某一句下中斷點,接著滑鼠右鍵,應該就有Debug Scenario可以選了。
by the way, 真的很高興,有讀者真的願意跟著文章練習 :)
也很感謝你的feedback,那可能代表文章哪邊講的不夠清楚。不過因為篇幅有限,真的很難完全介紹完,所以還是得K一點官方的document或sample,會比較容易瞭解囉