引子
回顧
雖然我很早以前就聽說單元測試,也曾經多次在項目中引入單元測試框架和單元測試的實踐為代碼質量的提升帶來了一絲助力。
但這種方式更多的是從軟件調試的角度出發,即將單元測試作為一種測試方法可用性的入口,而非從TDD、極限編程、或從"Fail Fast,Fix Fast”這種獲得快速反饋的方式來使用單元測試,使得實際過程中單元測試的效果並不明顯。
直到去年8月下旬開始參加極客學院的TDD實戰課才進一步深入了解基於TDD的單元測試的流程、方法和實踐的全過程,當時也間歇性的練習了一點Args等Kata項目,才逐步體會到TDD的妙處。
雖然到目前為止對於TDD的了解依然很淺,但在開發過程中,總是有意無意的“站在調用者的角度思考業務邏輯”,並盡可能的思考如何“編寫可測試的代碼”,總歸是一種進步。
我的教訓
總結自己學習TDD的一些經驗教訓:
- 需求的識別,總是按慣性一次性把整個需求全部提取了。
- 總是習慣於拿着代碼一把梭,沒有按照“Arange,Assert,Art”的步驟來規划任務。
- 步子邁得太大,方法拆得不夠細,過程式代碼的味道很濃,例如,在練習String Calculate過程中,就有非常明顯的問題。當然,這也是許多初學TDD開發者的通病。
- 沒有深刻理解“重構”的意義,只是把通過單元測試當做一個目的。
- Kata的練習頻率依然不高,一周只有兩到三次,每次不到一個小時。
- 單元測試和方法的命名不太規范,無法讓人產生直接的理解。
- 方法的代碼行較多,不符合優質代碼的標准。
- 方法間適當的空行(分段)很重要。
什么是TDD
定義
TDD的全稱是“測試驅動開發”,也是一種旨在提升代碼質量的開發實踐。這種開發實踐的主要步驟是在編寫產品代碼之前,先編寫單元測試代碼,然后再由測試代碼來決定寫什么產品代碼,其目的是取得快速反饋,並使用“illustrate the main line”方法來構建程序。
測試驅動開發是戴兩頂帽子思考的開發方式:先戴上實現功能的帽子,在測試的輔助下,快速實現其功能;再戴上重構的帽子,在測試的保護下,通過去除冗余的代碼,提高代碼質量。測試驅動着整個開發過程:首先,驅動代碼的設計和功能的實現;其后,驅動代碼的再設計和重構。
原則
在上述經驗教訓過程中,有些步驟其實與TDD的三原則相違背,讓我們來回顧一下這三個原則:
- 不允許編寫任何產品代碼,除非允許失敗的測試通過。
- 不允許編寫多余一個的失敗測試,編譯成功也是失敗。
- 不允許編寫多於恰好讓測試通過的產品代碼。
步驟
TDD其實也有一系列完整的操作流程,包括如下五個步驟:
- 添加一個小的測試
- 運行所有測試並且失敗
- 做一點修改
- 運行所有測試並且成功
- 重構以消除重復
可行性之爭
TDD也是一種充滿爭議的開發實踐,許多人都吐槽,這種方式在原本開發代碼之余,還得額外花三分之一的時間來編寫測試代碼。不過,我還是推薦《代碼整潔之道-程序員的自我修養》這本書中,Robert Bob大叔在第5.1小節說的幾句話:
1、此事已有定論!
2、爭論已經結束!
3、GOTO是有害的!
4、TDD確實可行。
他明確指出:
過去人們對TDD充滿爭議,就此發表了不少博客和文章,如今爭議依舊來襲。所不同的是,以前人們是認真嘗試着去批判和理解TDD,而現在只有誇誇奇談而已。結論很清楚,TDD的確切實可行,而且每個開發人員都要適應和掌握TDD。
TCR
在《極限中國社區》曾經介紹了一種測試驅動開發過程中的實踐模式,這種實踐被稱為“TCR”。
在實踐過程中,開發者始終保持着測試,成功則提交,失敗則回滾到上次的代碼這樣的循環。在並使用了插件來進行自動化回滾,確保每個方法的開發時間被控制在非常小的時間粒度上。這樣保證了開發者能夠以非常小的步子,非常快的頻率,實現代碼的開發過程。
Kata
優秀的代碼從來不是天生的,而是通過后天不斷的練習培養出來的,尤其是要想寫出符合面向對象設計的的好代碼,更是需要“刻意練習”。
Kata被人稱為是唯一的一種練習TDD的形式。
“Kata”是一種來自日語詞匯“形式”的翻譯,它描述了武術練習中,通過一種精心編排的動作模式,用來訓練自己達到肌肉記憶的水平。
Kata的練習例子是如此之多,只要你有心,總是能在海量的示例代碼中找到最適合自己的一個例子。
例如,我所使用的Roy Osherove設計的例子“String Calculate”就是一個非常不錯的示例。(當然,我給出的是一段反例代碼。)
1、An empty string returns zero
2、A single number returns the value
3、Two numbers, comma delimited, returns the sum
4、Two numbers, newline delimited, returns the sum
5、Three numbers, delimited either way, returns the sum
6、Negative numbers throw an exception
7、Numbers greater than 1000 are ignored
8、A single char delimiter can be defined on the first line (e.g. //# for a ‘#’ as the delimiter)
9、A multi char delimiter can be defined on the first line (e.g. //[###] for ‘###’ as the delimiter)
10、Many single or multi-char delimiters can be defined (each wrapped in square brackets)
當然,FizzBuzz或The Prime Factors Kata,Args也是一個非常不錯的示例。重要的並非例子本身,而是通過持續不斷的練習,形成自己的肌肉記憶。
在引文中,作者Peter Provost認為,最好的辦法:
我對人們的建議是連續兩周每天早上做一個30分鍾的練習。然后再選一個,每天做一次,堅持兩周
我不建議大家在工作中練習Kata,除非他們已經准備好了。你應該通過練習一周或六次,直到你已經決定對TDD的循環非常適應為止。否則,就像參加了一場沒有技術水平的比賽。