{TDD的目標}
Clean Code That Works
這句話的含義是,事實上我們只做兩件事情:讓代碼奏效(Work)和讓代碼潔凈(Clean),前者是把事情做對,后者是把事情做好。想想看,其實我們平時所做的所有工作,除去無用的工作和錯誤的工作以外,真正正確的工作,并且是真正有意義的工作,其實也就只有兩大類:增加功能和提升設計,而TDD 正是在這個原則上產生的。如果您的工作并非我們想象的這樣,(這意味著您還存在第三類正確有意義的工作,或者您所要做的根本和我們在說的是兩回事),那么這告訴我們您并不需要TDD,或者不適用TDD。而如果我們偶然猜對(這對于我來說是偶然,而對于Kent Beck和Martin Fowler這樣的大師來說則是辛勤工作的成果),那么恭喜您,TDD有可能成為您顯著提升工作效率的一件法寶。請不要將信將疑,若即若離,因為任何一項新的技術——只要是從根本上改變人的行為方式的技術——就必然使得相信它的人越來越相信,不信的人越來越不信。這就好比學游泳,唯一能學會游泳的途徑就是親自下去游,除此之外別無他法。這也好比成功學,即使把卡耐基或希爾博士的書倒背如流也不能擁有積極的心態,可當你以積極的心態去成就了一番事業之后,你就再也離不開它了。相信我,TDD也是這樣!想試用TDD的人們,請遵循下面的步驟:
編寫TestCase –> 實現TestCase –> 重構
(確定范圍和目標) (增加功能) (提升設計)
[友情提示:敏捷建模中的一個相當重要的實踐被稱為:Prove it With Code,這種想法和TDD不謀而合。]
{TDD的優點}
『充滿吸引力的優點』
完工時完工。表明我可以很清楚的看到自己的這段工作已經結束了,而傳統的方式很難知道什么時候編碼工作結束了。
全面正確的認識代碼和利用代碼,而傳統的方式沒有這個機會。
為利用你成果的人提供Sample,無論它是要利用你的源代碼,還是直接重用你提供的組件。
開發小組間降低了交流成本,提高了相互信賴程度。
避免了過渡設計。
系統可以與詳盡的測試集一起發布,從而對程序的將來版本的修改和擴展提供方便。
TDD給了我們自信,讓我們今天的問題今天解決,明天的問題明天解決,今天不能解決明天的問題,因為明天的問題還沒有出現(沒有TestCase),除非有TestCase否則我決不寫任何代碼;明天也不必擔心今天的問題,只要我亮了綠燈。
『不顯而易見的優點』
逃避了設計角色。對于一個敏捷的開發小組,每個人都在做設計。
大部分時間代碼處在高質量狀態,100%的時間里成果是可見的。
由于可以保證編寫測試和編寫代碼的是相同的程序員,降低了理解代碼所花費的成本。
為減少文檔和代碼之間存在的細微的差別和由這種差別所引入的Bug作出杰出貢獻。
在預先設計和緊急設計之間建立一種平衡點,為你區分哪些設計該事先做、哪些設計該迭代時做提供了一個可靠的判斷依據。
『有爭議的優點』
事實上提高了開發效率。每一個正在使用TDD并相信TDD的人都會相信這一點,但觀望者則不同,不相信TDD的人甚至堅決反對這一點,這很正常,世界總是這樣。
發現比傳統測試方式更多的Bug。
使IDE的調試功能失去意義,或者應該說,避免了令人頭痛的調試和節約了調試的時間。
總是處在要么編程要么重構的狀態下,不會使人抓狂。(兩頂帽子)
單元測試非常有趣。
{TDD的步驟}
編寫TestCase –> 實現TestCase –> 重構
(不可運行) (可運行) (重構)
步驟 制品
(1)快速新增一個測試用例 新的TestCase
(2)編譯所有代碼,剛剛寫的那個測試很可能編譯不通過 原始的TODO List
(3)做盡可能少的改動,讓編譯通過 Interface
(4)運行所有的測試,發現最新的測試不能編譯通過 -(Red Bar)
(5)做盡可能少的改動,讓測試通過 Implementation
(6)運行所有的測試,保證每個都能通過 -(Green Bar)
(7)重構代碼,以消除重復設計 Clean Code That Works
{FAQ}
[什么時候重構?]
如果您在軟件公司工作,就意味著您成天都會和想通過重構改善代碼質量的想法打交道,不僅您如此,您的大部分同事也都如此?墒,究竟什么時候該重構,什么情況下應該重構呢?我相信您和您的同事可能有很多不同的看法,最常見的答案是“該重構時重構”,“寫不下去的時候重構”,和“下一次迭代開始之前重構”,或者干脆就是“最近沒時間,就不重構了,下次有時間的時候重構吧”。正如您已經預見到我想說的——這些想法都是對重構的誤解。重構不是一種構建軟件的工具,不是一種設計軟件的模式,也不是一個軟件開發過程中的環節,正確理解重構的人應該把重構看成一種書寫代碼的方式,或習慣,重構時時刻刻有可能發生。在TDD中,除去編寫測試用例和實現測試用例之外的所有工作都是重構,所以,沒有重構任何設計都不能實現。至于什么時候重構嘛,還要分開看,有三句話是我的經驗:實現測試用例時重構代碼,完成某個特性時重構設計,產品的重構完成后還要記得重構一下測試用例哦。
[什么時候設計?]
這個問題比前面一個要難回答的多,實話實說,本人在依照TDD開發軟件的時候也常常被這個問題困擾,總是覺得有些問題應該在寫測試用例之前定下來,而有些問題應該在新增一個一個測試用例的過程中自然出現,水到渠成。所以,我的建議是,設計的時機應該由開發者自己把握,不要受到TDD方式的限制,但是,不需要事先確定的事一定不能事先確定,免得捆住了自己的手腳。
[什么時候增加新的TestCase?]
沒事做的時候。通常我們認為,如果你要增加一個新的功能,那么先寫一個不能通過的 TestCase;如果你發現了一個bug,那么先寫一個不能通過的TestCase;如果你現在什么都沒有,從0開始,請先寫一個不能通過的 TestCase。所有的工作都是從一個TestCase開始。此外,還要注意的是,一些大師要求我們每次只允許有一個TestCase亮紅燈,在這個 TestCase沒有Green之前不可以寫別的TestCase,這種要求可以適當考慮,但即使有多個TestCase亮紅燈也不要緊,并未違反TDD 的主要精神。
[TestCase該怎么寫?]
測試用例的編寫實際上就是兩個過程:使用尚不存在的代碼和定義這些代碼的執行結果。所以一個 TestCase也就應該包括兩個部分——場景和斷言。第一次寫TestCase的人會有很大的不適應的感覺,因為你之前所寫的所有東西都是在解決問題,現在要你提出問題確實不大習慣,不過不用擔心,你正在做正確的事情,而這個世界上最難的事情也不在于如何解決問題,而在于ask the right question!
[TDD能幫助我消除Bug嗎?]
答:不能!千萬不要把“測試”和“除蟲”混為一談!“除蟲”是指程序員通過自己的努力來減少bug的數量(消除bug這樣的字眼我們還是不要講為好^_^),而“測試”是指程序員書寫產品以外的一段代碼來確保產品能有效工作。雖然TDD所編寫的測試用例在一定程度上為尋找bug提供了依據,但事實上,按照TDD的方式進行的軟件開發是不可能通過TDD再找到bug的(想想我們前面說的“完工時完工”),你想啊,當我們的代碼完成的時候,所有的測試用例都亮了綠燈,這時隱藏在代碼中的bug一個都不會露出馬腳來。
但是,如果要問“測試”和“除蟲”之間有什么聯系,我相信還是有很多話可以講的,比如TDD事實上減少了bug的數量,把查找bug戰役的關注點從全線戰場提升到代碼戰場以上。還有,bug的最可怕之處不在于隱藏之深,而在于滿天遍野。如果你發現了一個用戶很不容易才能發現的bug,那么不一定對工作做出了什么杰出貢獻,但是如果你發現一段代碼中,bug的密度或離散程度過高,那么恭喜你,你應該拋棄并重寫這段代碼了。TDD避免了這種情況,所以將尋找bug的工作降低到了一個新的低度。
[我該為一個Feature編寫TestCase還是為一個類編寫TestCase?]
初學者常問的問題。雖然我們從TDD 的說明書上看到應該為一個特性編寫相應的TestCase,但為什么著名的TDD大師所寫的TestCase都是和類/方法一一對應的呢?為了解釋這個問題,我和我的同事們都做了很多試驗,最后我們得到了一個結論,雖然我不知道是否正確,但是如果您沒有答案,可以姑且相信我們。
我們的研究結果表明,通常在一個特性的開發開始時,我們針對特性編寫測試用例,如果您發現這個特性無法用TestCase表達,那么請將這個特性細分,直至您可以為手上的特性寫出TestCase為止。從這里開始是最安全的,它不會導致任何設計上重大的失誤。但是,隨著您不斷的重構代碼,不斷的重構 TestCase,不斷的依據TDD的思想做下去,最后當產品伴隨測試用例集一起發布的時候,您就會不經意的發現經過重構以后的測試用例很可能是和產品中的類/方法一一對應的。
[什么時候應該將全部測試都運行一遍?]
Good Question!大師們要求我們每次重構之后都要完整的運行一遍測試用例。這個要求可以理解,因為重構很可能會改變整個代碼的結構或設計,從而導致不可預見的后果,但是如果我正在開發的是一個ERP怎么辦?運行一遍完整的測試用例可能將花費數個小時,況且現在很多重構都是由工具做到的,這個要求的可行性和前提條件都有所動搖。所以我認為原則上你可以挑幾個你覺得可能受到本次重構影響的TestCase去run,但是如果運行整個測試包只要花費數秒的時間,那么不介意你按大師的要求去做。
[什么時候改進一個TestCase?]
增加的測試用例或重構以后的代碼導致了原來的TestCase的失去了效果,變得無意義,甚至可能導致錯誤的結果,這時是改進TestCase的最好時機。但是有時你會發現,這樣做僅僅導致了原來的TestCase在設計上是臃腫的,或者是冗余的,這都不要緊,只要它沒有失效,你仍然不用去改進它。記住,TestCase不是你的產品,它不要好看,也不要怎么太科學,甚至沒有性能要求,它只要能完成它的使命就可以了——這也證明了我們后面所說的“用Ctrl-C/Ctrl-V編寫測試用例”的可行性。
文章來源于領測軟件測試網 http://www.k11sc111.com/