摘 要:測試驅動開發是一種用于敏捷軟件開發的開發過程,可以快速應對需求變化。它要求先設計和編寫測試代碼,然后編寫功能代碼通過所有測試,再重構以提高代碼質量。文章將先介紹測試驅動開發的優點、使用環境,然后介紹開發過程,最后介紹相關工具。
關鍵詞:測試;TDD;敏捷開發
1 概述
1.1 定義
測試驅動開發(Test Driven Development, TDD)是由極限編程之父Kent Beck提出的一種面向對象的開發方法[1]。區別于傳統的軟件開發模式,測試先行將更重視測試在整個軟件開發過程中的作用并促進項目的進行。它要求先完成測試代碼,然后編寫功能代碼,并且功能代碼要以通過測試代碼為標準,然后對功能代碼重構,重構之后再運行測試并要通過測試[2]。它的一個開發周期比較短,整個項目是多個周期的迭代。這種開發方式有效的提高了軟件質量和開發效率[3]。目前,TDD已經被很多公司和開發團隊接受并用于實踐。
1.2 優點
由于測試先行,因此寫代碼前就應該有明確的需求,并體現在測試用例中。在交付前,測試用例可以用來描述功能需求并替代部分文檔。并且以測試用例描述的需求不容易出現模糊不清的概念,因為測試結果只會是True或False。這解決了開發人員在開發時誤解或由于溝通問題不完全理解需求文檔而造成開發到一定程度后才發現代碼與需求有偏差。但這還沒有解決對客戶的誤解或與客戶溝通不暢導致需求分析錯誤的問題。
功能代碼編寫完成后必須通過所有測試,這就保證了這部分代碼是滿足其功能要求的。通過確保每小部分代碼的質量,可以較快的疊加成更復雜的功能且保證最后交付的軟件與設計的要求是一致的。在對功能代碼進行優化時,因為也要通過測試用例,所以能保證這部分代碼的改動不會對調用它的其他模塊有影響。
1.3 適用環境
盡管從理論上講TDD可以在各種軟件開發項目中使用,但是在某些項目上可能感覺不到比較明顯的效率提升或質量提高。
TDD是面向對象的開發方式,如果項目不使用面向對象的設計和開發,則不適合使用TDD。
GUI等難以進行單元測試或進行測試比較麻煩的部分也不適宜使用TDD的開發方式。因為TDD是以測試驅動的,雖然測試是軟件交付內容的一部分,但是如果測試部分的難度大于軟件功能的開發,且消耗更多的時間,那么就無法再提高整個項目的開發效率。
TDD還對設計有很高的要求。設計的結果直接影響到測試用例,如果測試用例沒有被很好的設計,會嚴重影響軟件的開發。
2 應用
2.1 開發流程
2.1.1 設計
在各種開發方式中,設計都是非常重要的一個環節,它在很大程度上影響了軟件開發的難度、質量。在傳統的軟件設計方法中,需求分析的結果通常以文檔、UML圖、偽代碼等方式表示出來。在TDD中,一般可以使用測試用例來表示各個模塊的功能說明。測試用例可以明確地表示每個功能模塊要完成的功能和期望得到的結果,且不容易產生誤解。
設計單元測試用例時,一個比較難以把握的問題是測試用例的粒度。如果粒度太粗,可能會導致模塊內實現的功能過多、難以測試等問題,并沒有達到TDD的效果。TDD一般提倡小步前進。但也不能太細。粒度太細可能導致測試用例數量過多、維護測試用例過于麻煩。
2.1.2 創建測試用例
設計完測試用例以后,要實現測試用例。測試用例將保證功能代碼完成了設計的功能。在沒有寫功能代碼前,測試用例應該是無法通過的,因為需要完成的功能沒有完成。
測試用例還在一定程度上起到了文檔的作用。在TDD的開發方式中,測試用例可以說明某個功能模塊要完成的功能和它具有的接口。測試用例對于功能代碼的測試相當于黑盒測試,不用了解模塊內部的實現,只管它是否滿足需求。對于模塊內部要完成的功能,一般不寫測試用例。如要進行模塊內部的測試可以通過開發工具的調試功能來完成。測試用例主要測試模塊的功能。
2.1.3 編寫功能代碼
TDD的通常做法是測試代碼未編寫完成前是不寫功能代碼的,并且測試代碼寫完后是無法通過測試的。此時需要編寫功能代碼通過測試代碼。值得注意的是,功能代碼并不是越復雜越完善越好。最好的做法是所寫的功能代碼剛好通過所有測試用例。
編寫完代碼后要運行所有測試用例并保證全部通過。如果沒有全部通過,就要對代碼進行修改,直到通過所有測試。不應該在測試沒有全部通過的情況下去編寫其它代碼。
2.1.4 重構
常有人質疑TDD開發方式所開發的軟件質量。由于TDD在編寫功能代碼時只注重于快速完成代碼并通過所有測試,并沒有對代碼的質量進行檢驗,所以這里面的代碼質量無法得到保證。這樣的質疑并不是沒有道理。但是TDD的開發方式中也有比較重要的一個環節,重構。
在面向對象的開發方式中,幾乎都會比較重視重構。它能提高代碼的質量,利于以后對代碼的修改和維護。采用TDD的開發方式也應該有重構。重構不應該修改與外部的接口而應該重點優化內部實現的代碼。TDD有測試來驗證重構后的代碼不會影響外部接口。
在進行代碼修改以后,要重新運行所有測試,并保證全部通過。重構不是一次就能完成的,可能會多次進行,并在以后對項目進行修改的時候進行。這一步常被很多開發者忽略,因為重構不增加新的功能,在運行時可能不會有明顯的感覺。但對于提高代碼質量非常有幫助。無論是否使用TDD都應該重視重構。
2.1.5 交付和部署
當程序代碼編寫完成,并通過各種測試以后,軟件已經完成了客戶需要的功能。由于TDD在開發過程中用測試用例代替了部分需求說明文檔和規格文檔,省去了前期需求變化的過程中修改文檔的麻煩。在交付時可能需要補齊文檔。
今后如果需要對軟件進行修改,也只需要重復上述步驟。但也要保證所寫的代碼盡量簡單,測試覆蓋率要高,功能代碼需要通過全部測試。
2.2 應對需求變化
敏捷軟件開發是要能夠快速應對需求變化的。TDD作為極限編程中倡導的軟件開發方法也應該能夠快速應對需求變化。
除了敏捷軟件開發中要求的開發人員互相信任、經常面對面討論等要求可以快速響應需求變化外。當使用TDD時,如果需求發生變化,則要設計和修改測試代碼以反應需求的變化。當測試代碼改變后,可以根據運行測試后的結果快速的發現哪些模塊需要更改。只要修改代碼使測試通過就可以了,而不用擔心是不是又有其它地方出現了bug。TDD一個很大的好處也就是開發人員有明確的目標,代碼可以馬上進行測試并保證完成想要達到的功能,并通過測試覆蓋率了解到是否有多余的代碼。
2.3 工具
2.3.1 JUnit
JUnit是一個開源的測試框架,主要是用來對Java程序進行自動化單元測試的[4]。它能很好地集成在Eclipse中對代碼進行測試。它的主要功能有對測試結果斷言、共享測試數據、運行測試。
JUnit是xUnit中的一個,其它類似的還有NUnit用于測試.NET代碼,CppUnit測試C++代碼。
2.3.2 EasyMock
EasyMock是一個開源的生成Mock對象的工具,可以隔離測試對象與其它輔助的對象。它可以模擬出一個對象并記錄它期望進行的行為和結果。在回放狀態調用它以進行測試。還可以對Mock的對象進行驗證。
3 結束語
本文介紹了測試驅動開發的一些特點和實現過程,并介紹了相關工具。通過與持續集成等其它敏捷軟件開發工具和技術結合,可以較好地應對需求變化、及時發現問題并保證軟件質量。
參考文獻
[1]陳立群.測試驅動開發在J2EE項目中的全程實踐[J].計算機工程與科學,2008,30(4):86-88.
[2]侯典薈.基于.NET環境測試驅動開發研究與應用[D].大連理工大學,2006.
[3]Janzen D S, Saiedian H. On the influence of test-driven development on software design[C]. Turtle Bay,HI,United states: Institute of Electrical and Electronics Engineers Inc,2006.
[4]白凱,崔冬華.基于JUnit自動化單元測試的研究[J].計算機與數字工程,2010,38(2):52-54,103.