iT邦幫忙

DAY 26
3

30天快速上手TDD系列 第 26

[Day 26]User Story/ATDD/BDD/TDD - 總結

前幾篇文章提到了BDD的觀念,以及在.NET solution中,簡單的介紹了如何透過SpecFlow這個工具,來幫助我們減少在驗收測試案例與開始進行TDD中間的落差。

這篇文章則針對BDD的觀念,做個回顧與總結,希望讓讀者朋友們可以清楚的知道,怎麼運用BDD,來產生TDD一開始所需要的測試案例。

上一篇文章:[Day 25]BDD - TDD from BDD
本系列文章專區
@開發流程
先回顧一下前面文章:[Day 22]ATDD - ATDD的循環所提到的ATDD循環圖:

廢話不多說,這邊筆者直接透過連續的幾張圖,來幫助讀者瞭解,從一開始定義使用者需求開始,一路到最後通過所有測試,整個流程與彼此之間的對應關係。

並且遵守著本系列文最重要的宗旨:一切都是為了滿足使用者需求

@定義使用者需求
整個ATDD的流程,是先從「撰寫user story」或是「挑選一個user story」開始的,如下圖所示:

所以,首先先透過user story來定義使用者需求:

範例:

@Acceptance Test Cases 與 BDD Feature

  1. User story對應到BDD的部分,為Feature。
  2. 由user story break down,定義出acceptance test cases,其意義為如何驗證user story完成。

如下圖所示:

範例如下圖所示:

@Scenario
如同user story與acceptance test cases的關係,BDD透過scenario來代表這個feature應該要滿足哪些情境。

因此我們可以參考acceptance test cases,來擬定對應的scenario。如下圖所示:

範例如下圖所示:

@Given, When, Then
在BDD的scenario中,運用三個關鍵字來描述scenario,分別是:

  1. Given:代表在什麼前提、環境下
  2. When:當發生了什麼動作
  3. Then:預期有什麼樣的結果

如下圖所示:

有了這三個關鍵字所形成的description,基本上就可以透成描述一個scenario的基本要素。

@對應Unit Testing的3A原則
3A原則可以參考前面的文章:[Day 3]動手寫Unit Test

Unit Testing的3A原則對應到BDD scenario的關係如下:

  1. Given:對應到Unit Testing 3A中的Arrange
  2. When:對應到Unit Testing 3A中的Act
  3. Then:對應到Unit Testing 3A中的Assert

如下圖所示:

當我們已經透過BDD的工具(如SpecFlow),定義好feature與scenario,此時BDD library會自動幫我們建好了按照scenario的描述建立好scenario(其實就是測試案例)執行的flow。

接著只需要在scenario的三個關鍵字,對應到unit testing 3A原則,依照scenario的描述,去填入我們的TDD測試案例即可。

這時,我們TDD的測試案例與測試程式,已經不是單純的一個方法,而是一個對使用者來說,有意義的scenario,這個scenario是為了滿足使用者需求,所對應的user story,所break down出來的acceptance test cases。

透過使用DSL描述的scenario,將使用者需求,與TDD所要撰寫的系統程式碼連接在一起。

@TDD的紅燈
這時,我們已經清楚的定義好哪一些測試案例通過後,即代表滿足了使用者需求。

目前應該只有目標測試物件,可能也透過IDE工具,幫我們迅速建立好空的類別、屬性與方法。

因此,此時執行測試,應該會得到一堆測試失敗的結果。也就是TDD的紅燈。如下圖所示:

這個紅燈的意義,檯面上雖是測試失敗,但其本質意義則是:目標

這個目標,是完全符合使用者需求的,接著開發人員在撰寫、設計程式碼時,就可以心無旁騖,一心專注的滿足這個目標,也就是通過測試即可。

這正是TDD美妙之處:

  1. 讓開發人員不會分心、想太多而over design
  2. 讓開發出來的系統是滿足使用者需求
  3. 讓開發出來的程式,馬上就有最真實的測試保護
  4. 讓任何程式在撰寫完畢,都可以直接進行重構,而不必擔心重構後結果不如預期

@撰寫Production code,只為了通過測試
接下來開發人員只要專心的滿足測試案例,即代表滿足使用者需求。

換句話說,接下來是依照測試案例來撰寫系統的production code。如下圖所示:

這邊會有一個開發人員一開始無法突破的障礙:
「只滿足測試案例,就代表系統功能完整完成了嗎?不是吧?!要滿足測試案例的方法這麼多,但寫出來的code,有時甚至根本就不是系統功能要的,例如寫死hard-code的回傳結果,這能稱的上是一個好的系統、正常運作的系統嗎?」

這幾乎是每個開發人員從TAD(Test-After Development)轉換到TDD會碰到的門檻。

答案是:「對!只要滿足測試案例,就代表這個功能符合需求,這個功能可以正常運作」,但前提是:「測試案例夠完整,要能涵蓋到這個需求所有代表性的scenario」。

開發人員會有這樣的門檻,是因為有一個盲點:「切入角度是程式碼,而非用測試案例來代表使用者需求」。

事實上,每一行程式碼,每一個功能所存在的目的,就是為了滿足使用者需求,而使用者需求在寫程式前,已經被轉換成了測試案例。也就是程式碼只需要滿足測試案例、通過測試即可。

若有需求異動,代表需要新增或修改測試案例。
若有行為上的bug、defect,則代表測試案例涵蓋的不夠完整。

整套的TDD,會將測試案例的重要性拉到相當高,因為測試案例就代表著每個角色的共識,也是系統的最高目的:使用者的需求。

@使Production code通過TDD的測試案例
當Production code通過了TDD的測試案例,即代表滿足了該scenario。如下圖所示:

@滿足了Scenario,代表滿足了驗收測試案例
當scenario一一滿足,即代表一一滿足了驗收測試案例。因為在一開始設計scenario的時候,就是依據acceptance test cases而設計的。如下圖所示:

@滿足所有驗收測試案例,即代表滿足了user story
當所有驗收測試案例都通過了,就代表這個user story被完成了,也就代表這個使用者需求被完成了。如下圖所示:

基本上整個流程就如同一開始那張圖所示:

也就是下面這一系列的步驟:

  1. 先撰寫user story
  2. 為user story撰寫驗收測試案例
  3. 撰寫驗收測試程式
  4. 驗收測試結果失敗
  5. 撰寫整合測試案例
  6. 整合測試結果失敗
  7. 撰寫單元測試案例
  8. 單元測試結果失敗
  9. 撰寫實際程式
  10. 通過單元測試
  11. 重構單元測試涵蓋的範圍
  12. 重複step7~step11,直到整合測試通過
  13. 重複step5~step12,直到驗收測試通過
  14. 重複step3~step13,直到user story上的驗收測試案例全數通過
  15. user story完成,挑下一個user story

當眼前這個user story完成後,接著挑下一個user story,進入下一個ATDD/BDD/TDD的循環。

整個軟體開發的流程,也就如同前面所提到的W-model,如下圖所示:

註:每一段production code通過測試後,千萬別忘了還有個重要的步驟,叫做「重構」。重構只需滿足物件導向設計的原則,就相當足夠了。請參考前面 Day9~Day19 的文章。

@結論
本系列文章,從一開始到現在,總算把整套TDD所需用到的拼圖,都一一介紹完畢了。

TDD絕對不是只有「先寫測試」這麼單純、簡單。只有「先寫測試」,也完全無法體會到TDD的好處,反而容易造成開發人員認為:「為什麼我要多寫一份或多份程式,來驗證我已經知道的東西?」、「production code是我寫的,為什麼還要由我自己來驗證我自己的code對不對?」

如果TDD只是按照開發人員心中所想的系統,來先寫測試,那over design的問題仍在,仍可能寫出一堆華麗而無用的東西,仍可能累的要死寫出很讚的功能後,卻不是使用者要的。

另外,TDD的一個盲點就是,倘若一開始的切入角度都只在單元測試,那在實務應用上肯定會碰到問題,例如:TDD的測試案例不知道怎麼來、覺得測試程式是無用且多餘的、覺得TDD或測試程式一點都不實用。

那是因為TDD若著重在單元測試,則測試案例需要由開發人員自己發想,而自己想的測試案例,就跟自己寫的production code沒啥兩樣,那感覺就真的很多餘。

倘若TDD的測試案例,是由使用者與測試人員所定義,並取得開發人員共識後,由領域語言所描述,對應到TDD的測試案例,那麼這個測試案例的意義將完全不一樣,對開發人員來說,他也不是一份多餘無意義的工,而是定義出明確的目標,開發人員「只需要」滿足測試案例即可。

由於這一整套的TDD process,需要各角色的協同合作,因此強烈建議各開發團隊可以參考Scrum的流程,因為Scrum的角色、階段、開發進行方式,跟TDD的開發方式幾近於完全吻合。

而TDD被定義在extreme programming(XP)中,也不是完全獨立的,extreme programming其他的feature,正滿足了Agile的精神,整個XP的infrastructure,正是整個Scrum流程中,開發人員的運作引擎。

重視溝通、榮辱共享、擁抱變化、迅速回饋,目的只有一個:滿足使用者需求。

因此,任何需求的變化,都是軟體成長與演進的一個動力。Be a team,讓使用者或代表使用者的PO(Product Owner),也是團隊的一份子。整個團隊盡力support PO,但PO也是團隊的一份子,所以產品delay,也是大家的責任,尤其是PO要負最大責任。

這一整套開發方式,牽涉到開發流程(Scrum)、基礎建設(XP)、開發軟體精神(Agile),以及工程師的基本功力(OOD/OOP)。雖非一蹴可幾,但相關人員先將每一個拼圖都先學好、練好之後,最後只需要policy支持,再將每一塊拼圖拼起來,TDD真的沒這麼難,也沒這麼遙不可及,更不是個神話或烏托邦的世界。

(如果,連我們身在其中都不去瞭解,又怎麼讓manager或公司支持呢?又怎麼有資格來評論或批評,這樣的方法論一無是處或是空口說白話呢?)


上一篇
[Day 25]BDD - TDD from BDD
下一篇
[Day 27]TDD實戰練習-1
系列文
30天快速上手TDD31
0
海綿寶寶
iT邦大神 1 級 ‧ 2012-11-03 18:56:02

hatelove提到:
倘若TDD的測試案例,是由使用者與測試人員所定義,並取得開發人員共識後,由領域語言所描述,對應到TDD的測試案例,那麼這個測試案例的意義將完全不一樣,對開發人員來說,他也不是一份多餘無意義的工,而是定義出明確的目標,開發人員「只需要」滿足測試案例即可。

這或許是整件事是否能成局的關鍵之一
很希望有機會實際應用這種方式

看文中介紹的 SpecFlow 似乎只支援 .Net(Visaul Studio)
順著官網超連結逛到了支援多種程式語言的 Cucumber
看到他的介紹,不禁莞爾
上面是這麼寫的
1: Describe behaviour in plain text
2: Write a step definition in Ruby
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
7. Repeat 1-6 until the money runs out
哈哈

就是91 iT邦研究生 4 級 ‧ 2012-11-03 20:10:53 檢舉

恩恩,各語言通常都有類似的BDD library,您提到的那個黃瓜,我也覺得很讚 XD

0
ted99tw
iT邦高手 1 級 ‧ 2012-11-03 20:48:05

筆記筆記筆記筆記筆記

91大的鐵文就算印也要印出來收藏!!!

0
pajace2001
iT邦研究生 1 級 ‧ 2012-11-05 21:46:50

真是精采!!!!!太贊了拍手
剛剛 SpecFlow 一直都裝不起來,不過無意間發現 Windows Phone 也有BDD工具~~等等來試試看好了...汗

就是91 iT邦研究生 4 級 ‧ 2012-11-05 22:29:18 檢舉

讚

其實,BDD的原理真的也沒這麼難,簡單的說,就是把.feature檔,透過T4自動長成對應的測試程式。所以config中設定的unitTestProvider,就是選擇要用哪一個T4 template。

不過,在scenario上,設定中斷點,可以進去對應的程式碼裡面,這招就真的很酷...

我要留言

立即登入留言