先測試還是先代碼

        TDD不是傳統的任何一種自動化測試,更不是手工測試。首先,類似於傳統測試理論中的“系統集成測試和驗收測試”,TDD是針對系統需求而寫的。但是TDD往往是在你只有項目領域模型的class的“空殼”(沒有任何方法定義)的時候,你就去寫測試代碼,花2、3分鐘時間寫7、8行代碼(通常包括3、4行斷言代碼)使得編程看起來有點進展的。然後編譯!此時肯定出錯。這時候你可以在出錯的方法上面按 Ctrl+K+M (Visual Studio鍵盤模式)自動地在這個class內部生成方法存根,同時它會生成一行代碼拋出異常。此時如果你運行,仍然會得到異常。你接下來的任務就是修改這個異常語句,同樣地花2、3分鐘寫6、7行代碼使得它看起來有點進展。然後再運行,這時候發現異常是很正常的,沒有異常你就不能夠知道下一步該去修改哪裏。

       在這個過程中,程序編譯不能通過,或者編譯通過但是運行時發生異常,這是正常的,而且是非常必須的。這就是TDD跟傳統的編程方法的區別。

       傳統的編程方法,程序員自以爲很瞭解需求了,需求都寫在紙上而恐懼埋藏在心裏,然後程序員去花很多天來實現需求,直到最後貌似寫完了實現代碼也無法對代碼進行自動化測試。

        TDD方法,通常不用寫需求文檔,你首先用代碼來寫需求用例,你寫預想中的對象結構和方法如何被調用,以及功能單元運行的結果如何被斷言。之後就直接試圖編譯和運行它,每一次編譯或者運行錯誤都提示你應該補充什麼代碼。這時候程序員的任務就是讓實現代碼可以通過TDD測試,而不用把恐懼埋藏在心裏。

       在TDD過程中,通常你每隔5~10分鐘就要運行一次程序,否則可能你的開發過程就不夠敏捷,而是還是無法擺脫傳統的編程習慣的束縛(很多人在這點上不注意)。這非常重要。

        在你開始一個TDD測試用例的時候,首先要估計一下你產生單個TDD測試用例的“規模”。我認爲不超過1天開發工作量爲好。實際上我自己是大致1小時的工作做爲一個測試用例。

        TDD的原則之一是:不要做不必要的事情。你不要把一天之後纔會考慮的東西帶到編碼中。不要耗費精力做過多的設計。

        當你使得一個TDD測試用例可以運行通過,可以花一點時間(例如半個小時)進行小步驟的重構,把重複的中間變量刪除,增加一點臨時緩存。

        TDD的來源來自於“頭腦風暴”或者正規的需求分析,而不是你先編寫好實現代碼然後測試。恰好相反,TDD就是用可執行的代碼來寫需求(而不是用僞代碼,更不是寫在紙上那種需求文檔)。之後開發人員就進入讓TDD代碼可以運行通過的狀態,這樣編程就可以了。

        當客戶的需求變化,或者架構師的描述更深入細緻,於是你就可以寫下一個TDD測試用例,然後讓實現TDD使得它可以運行通過就OK了!

        注意,當需求變化時,不是去修改系統結構!你的新的TDD測試用例應該滿足新的需求描述,但是原來的TDD測試用例也應該正常地迴歸運行。久而久之,你就自然而然地成爲一個非常棒的架構師,而用不着去背誦那些號稱傳道架構、模式知識的書上的鬼話。

        使用TDD開發方式,當你隨時需要發佈產品時,2、3天之內就可以保證發佈出質量很穩定的版本,一點也不會恐懼擔心接下來的用戶挑剔的目光。你也可以經常拿里程碑的半成品給用戶試用。

        TDD是協調開發團隊的利器。好的XP團隊會有一個習慣,每一個人在Check In代碼之前都會運行不久前的TDD測試確保自己的要簽入的代碼跟以前的所有TDD用例相兼容。

         TDD可以輕易地統計開發進度、BUG產生和重新出現的次數、責任人、開發工作量等等。

         TDD不是傳統的白盒測試,它在你甚至還沒有寫出對象和其方法的定義代碼之前先就使用這個對象和方法進行斷言測試。
TDD不是傳統的黑盒測試,你需要每隔5~10分鐘就運行這個測試程序,通過改錯和重構來迭代地完成代碼。
在一個項目中TDD測試有上千個,你可以隨時迴歸運行,因此TDD不是傳統的手工測試,而是純粹的自動化測試。

 

        阻礙TDD深入使用的問題——在我看來即使Kent Beck在發明了TDD和XP開發之後的十多年,他本人也還是沒有解決這個問題——從GUI開始進行TDD。

       大多數人即使進行TDD開發,也是隻是在業務邏輯功能處理代碼方面。而如果能夠從GUI編碼就開始TDD,踩陣中能消除開發中的恐懼。

       但是應用程序的 UI 有很多種,有Web的、Win32的、.NET的、Mobile的、J2ME的、其它各種操作系統的,甚至Flex和Silverlight的,每一種的控件識別和模擬用戶操作流程的控制方法都有一點差別,例如Silverlight的幾乎所有操作都是異步的這就給許多人造成巨大的障礙,再例如在web應用中模擬一個查詢訂單並且錄入資料這樣的需要5、6步才能完成的操作。

        但是隻有解決了在 UI 編碼開發中使用TDD的問題,才能比較徹底地掌握TDD技術。

        現在大多數人迴避對 UI 部分使用TDD,只是用在後臺功能方面。這雖然也能大大提高產品質量和開發速度,但是畢竟還有很多糾紛靠開發完之後爭吵和主管拍腦袋來解決,而不是靠事先編寫大量TDD代碼來協調

 

        呵呵,我看到別人進行TDD開發最大障礙,不是他感覺困難,而是一棍子打死、聲稱這是理想模型。TDD是非常現實的,你不要做不必要的事情,例如不要去寫不必要的文檔。

       TDD是一種開發態度,而不是一個供人往臉上貼金的理論流派。我遇到別人在做一個項目時先說“先要把需求用例都描述清楚”的時候,我就請他本人來描述需求用例,結果無一例外地我遇到的這些人都不願意去描述用例——他們只是喜歡批評別人的測試用例。寫TDD是程序員的一種編程態度,喜歡TDD的程序員先在單元測試中寫TDD風格的測試用例,這只是一種很平常的事情,如果你實際去寫過幾十個,你就不再會覺得它是理想化的模型了,很多人會離不開這種開發方法。

我不太贊成傳統的單元測試的那種測試方法。當你過於糾纏於內部細節,重構的方便性就變小了。

        實際上我寧願寫300個TDD測試(TDD完全可以使用單元測試工具來寫),也不願意多寫30個過於糾纏內部細節的單元測試。TDD就是從需求出發,站在用戶的角度,或者站在產品經理的角度。有時候,我們把開發組織變爲以產品經理爲主導,而不是以技術經理爲主導。TDD是描述需求和進行架構重構的工具,而不是單單是測試。

        如果你只是寫傳統的單元測試,我覺得沒有必要。你在源代碼中寫斷言,這樣源代碼以DEBUG模式運行時就可以隨時自我測試每一個功能單元的內部邏輯,而無需單獨搞一個單元測試工程來測試。TDD測試可以驅動這些功能被執行,從而可以調用斷言被執行。

        TDD是在你實際動腦筋去想實現代碼之前,用來描述用戶觀點的代碼。所以只有TDD不可能用實現代碼中的斷言來代替,需要單獨使用一個TDD測試程序作爲引擎來驅動迴歸測試。

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