利用雙環 TDD 進行由外向內的開發

利用雙環 TDD 進行由外向內的開發

在我上一篇 文章 中,我開始討論倫敦派測試驅動開發(TDD),以及我認爲它和傳統 TDD 不一樣的兩個特色。第一個是利用雙環 TDD 進行由外向內的開發,我將在這篇文章中詳細討論。第二點是「說,而不是問」的面向對象設計,我將在 下一篇文章 中再做討論。前端

雙環 TDD

london_school_001

當你進行雙環 TDD 時,你在內環上花費的時間是以分鐘計的,而在外環上花費的時間是以小時或天計的。外環測試是從系統的外部用戶的角度來寫的,一般覆蓋了粗粒度的功能,而且已部署在真實的(或至少接近真實的)環境中。在 個人書中 我把這類測試稱之爲「指導測試」(Guiding Test),而 Freeman 和 Pryce 稱之爲 「驗收測試」(Acceptance Tests)。這些測試應當在客戶指望不能知足時失敗 —— 換而言之,它們提供了良好的回溯保護。它們也記載了系統應有的行爲。(另見個人文章「敏捷自動化測試設計的原則」)android

我不認爲雙環 TDD 是倫敦派 TDD 特有的,我相信傳統 TDD 開發者也會採用。這一理念早在 Kent Beck 的第一本關於極限編程的書中就存在了。但我認爲倫敦派的獨到之處在於由外向內的設計,而且輔之以 mock 的使用。ios

由外向內的設計

若是你使用雙環 TDD,一般你會先寫一個指導測試來體現一個用戶是如何與你的系統交互的。這個測試會幫助你肯定位於最頂層,被首先調用的,做爲需求功能的入口點的函數或類。這經常是一個 GUI 組件,一個網頁上的連接,或是一個命令行標誌。git

而對倫敦派 TDD 而言,等你開始設計那些由該 GUI 組件、網頁連接或是命令行標誌來調用的內環 TDD 的類或方法的時候,你很快就會意識到這些新的代碼沒法由本身來實現整塊功能,而是須要其它的協做類來共同完成。github

london_school_003

用戶觀察系統,而且指望某些功能。這意味着系統的邊界須要一個新的類。而這個類又進而須要更多還沒有存在的協做類。編程

這些協做類尚不存在,或者至少不能提供你須要的所有功能。與其在此時暫停 TDD 而去馬上開發這些新的類,你其實能夠在測試中將它們替換爲 mock。在你將接口和協議開發到知足需求以前,更換 mock 和實驗代碼一般是很容易的。如此一來,當你在設計測試用例的同時,你也在設計生產代碼了。後端

london_school_004

你能夠將協做對象替換爲 mock,這樣你就能設計它們之間的接口和協議了。架構

當你對你的設計滿意了,而且測試也經過了之後,你就能夠深刻下一層開始真正實現一個協做類。固然,若是某個類又進一步須要其它協做者,你能夠再將它們替換爲 mock 來進一步設計這些接口。這一方法能夠持續整個系統設計,通達各個架構層和抽象層。ide

london_school_005

你已經完成了系統邊界的類,如今你能夠開發它的某個協做類,而且用 mock 替換這個類進一步須要的協做類。函數

這一工做方式可讓你把問題分解成可控的部件,在你每開始一個新部件以前都能把當前的部件詳細規定、充分測試。你能從關注用戶需求開始,而後由外向內地構建,在系統中一個部件一個部件地追蹤用戶交互的全過程,直到指導測試能夠經過。一般在指導測試中不會將系統的部件替換成 mock,這樣最終當指導測試經過的時候你就能夠確信你沒有忘記實現任何一個協做類。

在傳統 TDD 中由外向內

在傳統 TDD 的方法中也能夠由外向內,可是用一種幾乎不須要 mock 的方法。存在幾種不一樣的策略來解決「協做類尚不存在」的問題。其中一種是從退化的用例開始設計,此時從用戶視角來看幾乎什麼都沒發生。這是一種當輸出比實際用例,或者愉快路徑要簡單得多的時候的特例。這樣你就能只用最基礎的空實現,或者假的返回值來構建這一簡化版的功能需求所須要的類的結構和方法。一旦結構有了,你就能夠充實它(或許由內而外地進行也行)。

另一種在傳統 TDD 中由外向內的策略是,先由外向內地寫測試,而當你發現你沒法在某個協做類被實現以前使測試經過之時,就註釋掉那個測試,轉而去實現所需的協做類。最終你會發現你能夠僅憑已經存在的協做者,就徹底實現某個類,由此再逐步向上實現。

由外向內有時在傳統 TDD 方法中也許根本行不通。你會從系統中心的某個類開始,挑出某個僅憑已有的協做者就能徹底實現和測試的部件。這一般是應用的領域模型的中心的一個類。當它完成之後,你再由中心向外繼續開發系統,一個一個地添加新的類。由於只使用已有的類,你就幾乎不須要使用 mock。最終你也會發現你完成了全部功能,也經過了指導測試。

優缺點

我認爲由外向內的方法是有顯著優點的。它能幫助你持續關注用戶的真正所需,使你構建一些真正有用的東西,而避免浪費時間粉飾打磨用戶不須要的。我認爲不管對傳統 TDD 仍是倫敦派 TDD 來講,由外向內的方法都須要技巧和訓練。學會如何將功能拆解成你能一步一步來開發和設計的增量部件並不是易事。可是若是你由中心向外工做,就存在你會構建用戶不須要的東西的風險,或者當你抵達外層卻最終發現系統並不適用,而不得不進行重構。

然而,假設你已是由外向內工做的了,我仍認爲,取決於你是在真正的生產代碼中編寫假的實現,仍是隻在 mock 中寫,這二者是有所不一樣的。若是你在生產代碼中寫,你就逐步須要把它們取代爲真正的功能。而若是你把假的功能放在 mock 中,它們就能永遠存在於測試代碼中,即便當真的功能已經實現了,它們還在那兒。這對於程序文檔頗有用,也能讓你的測試得以繼續快速執行。

話雖如此,也存在一些關於在測試中使用了不少 mock 以後的可維護性的爭議。當設計更改時,除了生產代碼外還要更新全部的 mock 也許代價太大了。一旦真正的實現完成了,或許內環測試就應該被刪除?畢竟指導測試已經能提供你須要的所有回溯保護了,所以那些僅僅對你最初的設計有用的測試並不值得保留?我不以爲這樣作就是毫無指摘的。從我和一些倫敦派支持者的討論來看,即便他們也會刪除部分的測試,但他們並不會刪除全部使用了 mock 的測試。

我也仍在嘗試理解這些爭端,而且試着找出在怎樣的場合裏倫敦派 TDD 能夠帶來最大的收益。我但願我已經概述了由外向內的開發中,各類方法的區別。在個人下篇文章中,我將探討倫敦派 TDD 是如何推廣「說,而不是問」的面向對象設計 的。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索