TDD的三條軍規 (原文最終修訂於 2006-04-09 晚上09:45:01)

這些年來,我喜歡用下面這三條簡單的規則來描述測試驅動開發:

  1. 除非這能讓失敗的單元測試通過,否則不允許去編寫任何的產品代碼。
  2. 只允許編寫剛好能夠導致失敗的單元測試。 (編譯失敗也屬於一種失敗)
  3. 只允許編寫剛好能夠導致一個失敗的單元測試通過的產品代碼。

對於任何功能,一定要從編寫它的單元測試開始;但是到了原則2,你就不能再爲那個單元測試寫更多內容。只要一出現該單元測試代碼編譯失敗,或是斷言失敗,你就必須停下來開始編寫產品代碼;但是到了原則3,你就只能編寫產品代碼,直到讓測試編譯成功或通過斷言爲準。

仔細想想,就會發現如果不是去讓一些東西編譯或是執行,你就根本沒辦法去寫代碼。確實,這也正是關鍵所在。我們所做的任何事情(無論是寫測試,寫產品代碼,或是重構),都要保持系統能夠一直運行。跑通測試的時間間隔應該是以秒或是分鐘級的,即使那只有10分鐘,也都太長了。

想了解實際的操作過程,可以看看“保齡球遊戲中的Kata(譯註2)一文。

現今有很多程序員,每當他們頭一次聽到這種技術的時候,會想:“這種做法太愚蠢了!”“這會讓我慢下來,這是時間和精力的浪費,讓我無法思考,無法設計,還會打亂我的思路。”然而,試想,當你走進一個房間,裏面都是用這種方式工作的人們,會發生什麼情況?你隨便選個時間,隨意找個人,一分鐘以前,他們所有的代碼都跑通過。

讓我再重複一遍:一分鐘以前每個人的代碼都能跑通!不管你找誰,也不管你何時去找,一分鐘以前他們所有的代碼都跑通了!

如果你所有的代碼自始至終都能跑通,那麼你會多長時間用一次調試器?答案是,不會太經常。只要敲幾下^Z就能很容易的恢復這些代碼到跑通時的狀態,然後再試着把前幾分鐘的代碼寫一遍即可。而如果你不經常調試,那麼會省去多少時間呢?而現在你花在調試上的時間又有多少呢?一旦你有調試過,你又要花上多少時間來修復這些bug呢?如果你能夠大大減少這些時間的話,又會如何呢?

好處還不只這些。如果你採用這種方法,那麼每小時你都會產生好幾個測試;每天就有十幾個;每個月就幾百個;一年下來,你所編寫的測試就會有數千個。你可以保留着這些測試而且在任何你希望的時候去運行它們!什麼時候運行它們呢?隨時!只要你做了改動,就去運行它們!

有些代碼已經混亂不堪了,可是我們爲何不去清理它們呢?我們擔心會破壞它們。但如果我們擁有測試,我們就有理由確信這些代碼不會被破壞,或是說我們可以很快的就找到被破壞的地方。如果我們有了這些測試,我們就對代碼發生改變無所忌憚。如果我們看到混亂的代碼,或是一個不清晰的結構,可以毫無顧慮地清理它。因爲有了測試,代碼重新變得易修改了;因爲有了測試,軟件重新變“軟”了。

好處還不只這些。如果你想要知道如何去調用一個特定的API,就會有一個測試能夠告訴你;如果你想要知道如何創建一個特定的對象,就會有個測試能告訴你。你想知道的任何有關這個系統的,都有一個測試能夠去示意。這些測試就像是小型的設計文檔,小型的代碼示例,描述了系統的工作和使用方式。

你是否曾經整合過一個第三方庫到你的項目中呢?你拿到一本厚厚的精緻的幫助文檔,在結尾處,有一沓薄薄的示例附錄。你會選擇哪個去閱讀呢?當然是示例了!那就是單元測試啊!它們是這份文檔中最有用的部分;它們是如何使用代碼的鮮活的例子;它們是極其詳細、完全沒有歧義的設計文檔,相當的正規甚至可以執行,而且不會與產品代碼相脫離。

好處還不只這些。如果你曾有過增加單元測試到一個可以工作的系統中的經歷,你會發現那一點兒都不好玩。你很可能會發現想跑通測試,你一定是要麼去改變系統中的部分設計,要麼就在測試上作假。這是因爲你試圖去測試的系統並不是基於可測試設計的。例如,你想要測試某個函數f。然而,f調用了另外一個從數據庫中刪除記錄的函數。在你的測試中,你不希望這條記錄被刪掉,但卻沒有辦法來阻止它。這樣的系統就不是基於可測試設計的系統。

當你遵循TDD的三條規則的時候,你的所有代碼天生就可測試!而且另一個能形容“可測試”的詞彙就是“可解耦”。爲了單獨的測試一個模塊,你就必須把它解耦。所以TDD強制你去解耦這些模塊。確實,如果你遵循這三條規則的話,你會發現你可能比起從前來能做出更多的解藕。這就強制了你去創造更好的,低耦合的設計。

收穫了所有的這些好處,這些愚蠢的小規則實際上好像就沒那麼愚蠢了吧。他們實際上很可能是一些基本而又深刻的原則。確實,在我接觸TDD之前我曾做過將近30年的程序員,我不認爲曾有過什麼人教過我什麼非常奏效但又基本的編程實踐。畢竟三十年意味着很多的經驗。但當我開始了使用TDD,我就被這項技術的有效性所震撼,也沉迷其中。我不會再考慮去敲一大堆代碼然後平白指望它們能運行成功。對於那種先將一組模塊打散,然後希望能夠再將它們整合起來,並且讓它們在能下個禮拜五前運行起來的做法,我也不再能忍受。在我寫程序的時候,每一個決定都由這種讓現在開始寫的代碼能在一分鐘後再次執行這樣的基本需求所驅使着。

 

譯註:

1,TDD,測試驅動開發。

2,Kata,可理解爲武功招式,詳細解釋可參見譯者的上篇譯文

小注:本篇文章由鄧輝先生親自校正,並給予了很多中肯而深刻的建議,譯者在此給予感謝!

 (原文鏈接網址:http://www.butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd; Robert C. Martin的英文blog網址: http://www.butunclebob.com/ArticleS.UncleBob 

譯者注:Robert C. MartinObject Mentor公司總裁,面向對象設計、模式、UML、敏捷方法學和極限編程領域內的資深顧問。他不僅是Jolt獲獎圖書《敏捷軟件開發:原則、模式與實踐》(中文版)(《敏捷軟件開發》(英文影印版))的作者,還是暢銷書Designing Object-Oriented C++ Applications Using the Booch Method的作者。MartinPattern Languages of Program Design 3More C++ Gems的主編,並與James Newkirk合著了XP in Practice。他是國際程序員大會上著名的發言人,並在C++ Report雜誌擔任過4年的編輯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章