[30天快速上手TDD][Day 26]User Story/ATDD/BDD/TDD - 總結
前言
前幾篇文章提到了 BDD 的觀念,以及在 .NET solution 中,簡單的介紹了如何透過 SpecFlow 這個工具,來幫助我們減少在驗收測試案例與開始進行 TDD 中間的落差。
這篇文章則針對 BDD 的觀念,做個回顧與總結,希望讓讀者朋友們可以清楚的知道,怎麼運用 BDD ,來產生 TDD 一開始所需要的測試案例。
開發流程
先回顧一下前面文章: [30天快速上手TDD][Day 22]ATDD - ATDD的循環 所提到的 ATDD 循環圖:
廢話不多說,這邊筆者直接透過連續的幾張圖,來幫助讀者瞭解,從一開始定義使用者需求開始,一路到最後通過所有測試,整個流程與彼此之間的對應關係。
並且遵守著本系列文最重要的宗旨:一切都是為了滿足使用者需求。
定義使用者需求
整個 ATDD 的流程,是先從「撰寫 user story」或是「挑選一個 user story」開始的,如下圖所示:
所以,首先先透過 user story 來定義使用者需求:
範例:
Acceptance Test Cases 與 BDD Feature
- User story 對應到 BDD 的部分,為 Feature 。
- 由 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 ,分別是:
- Given: 代表在什麼前提、環境下
- When: 當發生了什麼動作
- Then: 預期有什麼樣的結果
如下圖所示:
有了這三個關鍵字所形成的 description ,基本上就可以透成描述一個 scenario 的基本要素。
對應 Testing 的 3A 原則
3A 原則可以參考前面的文章: [30天快速上手TDD][Day 3]動手寫 Unit Test
Testing 的 3A 原則對應到 BDD scenario 的關係如下:
- Given: 對應到 Unit Testing 3A 中的 Arrange
- When: 對應到 Unit Testing 3A 中的 Act
- Then: 對應到 Unit Testing 3A 中的 Assert
當我們已經透過 BDD 的工具(如 SpecFlow ),定義好 feature 與 scenario ,此時 BDD library 會自動幫我們按照 scenario 的描述,來建立 scenario(其實就是測試案例)執行的 flow。
接著只需要在 scenario 的三個關鍵字,對應到 Testing 3A 原則,依照 scenario 的描述,去填入我們的 TDD 測試案例即可。
這時,我們 TDD 的測試案例與測試程式,已經不是單純的一個方法,而是一個對使用者來說,有意義的 scenario ,這個 scenario 是為了滿足使用者需求,所對應的 user story ,所 break down 出來的 acceptance test cases 。
透過使用 DSL 描述的 scenario ,將使用者需求,與 TDD 所要撰寫的系統程式碼連接在一起。
TDD 的紅燈
這時,我們已經清楚的定義好哪一些測試案例通過後,即代表滿足了使用者需求。
目前應該只有目標測試物件,可能也透過 IDE 工具,幫我們迅速建立好空的類別、屬性與方法。
因此,此時執行測試,應該會得到一堆測試失敗的結果。也就是 TDD 的紅燈。如下圖所示:
這個紅燈的意義,檯面上雖是測試失敗,但其本質意義則是:目標!
這個目標,是完全符合使用者需求的,接著開發人員在撰寫、設計程式碼時,就可以心無旁騖,一心專注的滿足這個目標,也就是通過測試即可。
這正是 TDD 美妙之處:
- 讓開發人員不會分心、想太多而 over design
- 讓開發出來的系統是滿足使用者需求
- 讓開發出來的程式,馬上就有最真實的測試保護
- 讓任何程式在撰寫完畢,都可以直接進行重構,而不必擔心重構後結果不如預期
撰寫 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 被完成了,也就代表這個使用者需求被完成了。如下圖所示:
基本上整個流程就如同一開始那張圖所示:
也就是下面這一系列的步驟:
- 先撰寫 user story
- 為 user story 撰寫驗收測試案例
- 撰寫驗收測試程式
- 驗收測試結果失敗
- 撰寫整合測試案例
- 整合測試結果失敗
- 撰寫單元測試案例
- 單元測試結果失敗
- 撰寫實際程式
- 通過單元測試
- 重構單元測試涵蓋的範圍
- 重複 step7 ~ step11 ,直到整合測試通過
- 重複 step5 ~ step12 ,直到驗收測試通過
- 重複 step3 ~ step13 ,直到 user story 上的驗收測試案例全數通過
- User story 完成,挑下一個 user story
當眼前這個 user story 完成後,接著挑下一個 user story ,進入下一個 ATDD / BDD / TDD 的循環。
整個軟體開發的流程,也就如同前面所提到的 W-Model ,如下圖所示:
圖片來源:http://sqa.fyicenter.com/FAQ/Software-Development-Models/models_3.JPG
每一段 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 或公司支持呢?又怎麼有資格來評論或批評,這樣的方法論一無是處或是空口說白話呢?
blog 與課程更新內容,請前往新站位置:http://tdd.best/