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