|
測(cè)試驅(qū)動(dòng)開發(fā)(Test-Driven Development,TDD)是通過測(cè)試定義所要開發(fā)的功能的接口,然后實(shí)現(xiàn)功能的開發(fā)過程。
Test-Driven Development(TDD),是Extreme Programming (XP)--極限編程的一個(gè)重要組成部分。
在上面的圖中,列出的的是XP的12個(gè)團(tuán)隊(duì)實(shí)踐。Test-Driven Development是其中之一。 Kent Beck 的著作TDD(Test Driven Development) 中詳細(xì)講述了測(cè)試驅(qū)動(dòng)開發(fā)。 當(dāng)你使用TDD的時(shí)候一定要說明是測(cè)試驅(qū)動(dòng)開發(fā)還是測(cè)試驅(qū)動(dòng)設(shè)計(jì)。這兩者是有區(qū)別的。測(cè)試驅(qū)動(dòng)開發(fā),是通過測(cè)試定義所要開發(fā)的功能的接口,然后實(shí)現(xiàn)功能的開發(fā)過程。對(duì)于測(cè)試驅(qū)動(dòng)設(shè)計(jì),在XP中似乎已經(jīng)消失了,而是被測(cè)試驅(qū)動(dòng)開發(fā)所取代。另外在XP中有用于描述設(shè)計(jì)的,SimpleDesign ,Design Improvement.
1) 明確當(dāng)前要完成的功能??梢杂涗洺梢粋€(gè) TODO 列表。 二、測(cè)試驅(qū)動(dòng)開發(fā)的原則 1)測(cè)試隔離。不同代碼的測(cè)試應(yīng)該相互隔離。對(duì)一塊代碼的測(cè)試只考慮此代碼的測(cè)試,不要考慮其實(shí)現(xiàn)細(xì)節(jié)(比如它使用了其他類的邊界條件)。 2)一頂帽子。開發(fā)人員開發(fā)過程中要做不同的工作,比如:編寫測(cè)試代碼、開發(fā)功能代碼、對(duì)代碼重構(gòu)等。做不同的事,承擔(dān)不同的角色。開發(fā)人員完成對(duì)應(yīng)的工作時(shí)應(yīng)該保持注意力集中在當(dāng)前工作上,而不要過多的考慮其他方面的細(xì)節(jié),保證頭上只有一頂帽子。避免考慮無關(guān)細(xì)節(jié)過多,無謂地增加復(fù)雜度。 3)測(cè)試列表。需要測(cè)試的功能點(diǎn)很多。應(yīng)該在任何階段想添加功能需求問題時(shí),把相關(guān)功能點(diǎn)加到測(cè)試列表中,然后繼續(xù)手頭工作。然后不斷的完成對(duì)應(yīng)的測(cè)試用例、功能代碼、重構(gòu)。一是避免疏漏,也避免干擾當(dāng)前進(jìn)行的工作。 4)測(cè)試驅(qū)動(dòng)。這個(gè)比較核心。完成某個(gè)功能,某個(gè)類,首先編寫測(cè)試代碼,考慮其如何使用、如何測(cè)試。然后在對(duì)其進(jìn)行設(shè)計(jì)、編碼。 5)先寫斷言。測(cè)試代碼編寫時(shí),應(yīng)該首先編寫對(duì)功能代碼的判斷用的斷言語句,然后編寫相應(yīng)的輔助語句。 6)可測(cè)試性。功能代碼設(shè)計(jì)、開發(fā)時(shí)應(yīng)該具有較強(qiáng)的可測(cè)試性。其實(shí)遵循比較好的設(shè)計(jì)原則的代碼都具備較好的測(cè)試性。比如比較高的內(nèi)聚性,盡量依賴于接口等。 7)及時(shí)重構(gòu)。無論是功能代碼還是測(cè)試代碼,對(duì)結(jié)構(gòu)不合理,重復(fù)的代碼等情況,在測(cè)試通過后,及時(shí)進(jìn)行重構(gòu)。 三、測(cè)試驅(qū)動(dòng)開發(fā)的測(cè)試范圍 按大師 Kent Benk 的話,對(duì)那些你認(rèn)為應(yīng)該測(cè)試的代碼進(jìn)行測(cè)試,測(cè)試驅(qū)動(dòng)開發(fā)強(qiáng)調(diào)測(cè)試并不應(yīng)該是負(fù)擔(dān),而應(yīng)該是幫助我們減輕工作量的方法。 四、TDD的優(yōu)點(diǎn) 『充滿吸引力的優(yōu)點(diǎn)』 完工時(shí)完工。表明我可以很清楚的看到自己的這段工作已經(jīng)結(jié)束了,而傳統(tǒng)的方式很難知道什么時(shí)候編碼工作結(jié)束了。 『不顯而易見的優(yōu)點(diǎn)』 逃避了設(shè)計(jì)角色。對(duì)于一個(gè)敏捷的開發(fā)小組,每個(gè)人都在做設(shè)計(jì)。 『有爭(zhēng)議的優(yōu)點(diǎn)』 事實(shí)上提高了開發(fā)效率。每一個(gè)正在使用TDD并相信TDD的人都會(huì)相信這一點(diǎn),但觀望者則不同,不相信TDD的人甚至堅(jiān)決反對(duì)這一點(diǎn),這很正常,世界總是這樣。 五、TDD的 優(yōu)勢(shì) TDD的基本思路就是通過測(cè)試來推動(dòng)整個(gè)開發(fā)的進(jìn)行。而測(cè)試驅(qū)動(dòng)開發(fā)技術(shù)并不只是單純的測(cè)試工作。 需求向來就是軟件開發(fā)過程中感覺最不好明確描述、易變的東西。這里說的需求不只是指用戶的需求,還包括對(duì)代碼的使用需求。很多開發(fā)人員最害怕的就是后期還要修改某個(gè)類或者函數(shù)的接口進(jìn)行修改或者擴(kuò)展,為什么會(huì)發(fā)生這樣的事情就是因?yàn)檫@部分代碼的使用需求沒有很好的描述。測(cè)試驅(qū)動(dòng)開發(fā)就是通過編寫測(cè)試用例,先考慮代碼的使用需求(包括功能、過程、接口等),而且這個(gè)描述是無二義的,可執(zhí)行驗(yàn)證的。 通過編寫這部分代碼的測(cè)試用例,對(duì)其功能的分解、使用過程、接口都進(jìn)行了設(shè)計(jì)。而且這種從使用角度對(duì)代碼的設(shè)計(jì)通常更符合后期開發(fā)的需求。可測(cè)試的要求,對(duì)代碼的內(nèi)聚性的提高和復(fù)用都非常有益。因此測(cè)試驅(qū)動(dòng)開發(fā)也是一種代碼設(shè)計(jì)的過程。 開發(fā)人員通常對(duì)編寫文檔非常厭煩,但要使用、理解別人的代碼時(shí)通常又希望能有文檔進(jìn)行指導(dǎo)。而測(cè)試驅(qū)動(dòng)開發(fā)過程中產(chǎn)生的測(cè)試用例代碼就是對(duì)代碼的最好的解釋。 快樂工作的基礎(chǔ)就是對(duì)自己有信心,對(duì)自己的工作成果有信心。當(dāng)前很多開發(fā)人員卻經(jīng)常在擔(dān)心:“代碼是否正確?”“辛苦編寫的代碼還有沒有嚴(yán)重bug?”“修改的新代碼對(duì)其他部分有沒有影響?”。這種擔(dān)心甚至導(dǎo)致某些代碼應(yīng)該修改卻不敢修改的地步。測(cè)試驅(qū)動(dòng)開發(fā)提供的測(cè)試集就可以作為你信心的來源。 當(dāng)然測(cè)試驅(qū)動(dòng)開發(fā)最重要的功能還在于保障代碼的正確性,能夠迅速發(fā)現(xiàn)、定位bug。而迅速發(fā)現(xiàn)、定位bug是很多開發(fā)人員的夢(mèng)想。針對(duì)關(guān)鍵代碼的測(cè)試集,以及不斷完善的測(cè)試用例,為迅速發(fā)現(xiàn)、定位bug提供了條件。 我的一段功能非常復(fù)雜的代碼使用TDD開發(fā)完成,真實(shí)環(huán)境應(yīng)用中只發(fā)現(xiàn)幾個(gè)bug,而且很快被定位解決。您在應(yīng)用后,也一定會(huì)為那種自信的開發(fā)過程,功能不斷增加、完善的感覺,迅速發(fā)現(xiàn)、定位bug的能力所感染,喜歡這個(gè)技術(shù)的。 那么是什么樣的原理、方法提供上面說的這些好處哪?下面我們就看看TDD的原理。 六、TDD的原理 測(cè)試驅(qū)動(dòng)開發(fā)的基本思想就是在開發(fā)功能代碼之前,先編寫測(cè)試代碼。也就是說在明確要開發(fā)某個(gè)功能后,首先思考如何對(duì)這個(gè)功能進(jìn)行測(cè)試,并完成測(cè)試代碼的編寫,然后編寫相關(guān)的代碼滿足這些測(cè)試用例。然后循環(huán)進(jìn)行添加其他功能,直到完全部功能的開發(fā)。 我們這里把這個(gè)技術(shù)的應(yīng)用領(lǐng)域從代碼編寫擴(kuò)展到整個(gè)開發(fā)過程。應(yīng)該對(duì)整個(gè)開發(fā)過程的各個(gè)階段進(jìn)行測(cè)試驅(qū)動(dòng),首先思考如何對(duì)這個(gè)階段進(jìn)行測(cè)試、驗(yàn)證、考核,并編寫相關(guān)的測(cè)試文檔,然后開始下一步工作,最后再驗(yàn)證相關(guān)的工作。下圖是一個(gè)比較流行的測(cè)試模型:V測(cè)試模型。
【圖 V測(cè)試模型】 在開發(fā)的各個(gè)階段,包括需求分析、概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)、編碼過程中都應(yīng)該考慮相對(duì)應(yīng)的測(cè)試工作,完成相關(guān)的測(cè)試用例的設(shè)計(jì)、測(cè)試方案、測(cè)試計(jì)劃的編寫。這里提到的開發(fā)階段只是舉例,根據(jù)實(shí)際的開發(fā)活動(dòng)進(jìn)行調(diào)整。相關(guān)的測(cè)試文檔也不一定是非常詳細(xì)復(fù)雜的文檔,或者什么形式,但應(yīng)該養(yǎng)成測(cè)試驅(qū)動(dòng)的習(xí)慣。 關(guān)于測(cè)試模型,還有X測(cè)試模型。這個(gè)測(cè)試模型,我認(rèn)為,是對(duì)詳細(xì)階段和編碼階段進(jìn)行建模,應(yīng)該說更詳細(xì)的描述了詳細(xì)設(shè)計(jì)和編碼階段的開發(fā)行為。及針對(duì)某個(gè)功能進(jìn)行對(duì)應(yīng)的測(cè)試驅(qū)動(dòng)開發(fā)。
【圖 X測(cè)試模型】 基本原理應(yīng)該說非常簡(jiǎn)單,那么如何進(jìn)行實(shí)際操作哪,下面對(duì)開發(fā)過程進(jìn)行詳細(xì)的介紹。 七、測(cè)試技術(shù) 1. 測(cè)試范圍、粒度 對(duì)哪些功能進(jìn)行測(cè)試?會(huì)不會(huì)太繁瑣?什么時(shí)候可以停止測(cè)試?這些問題比較常見。按大師 Kent Benk 的話,對(duì)那些你認(rèn)為應(yīng)該測(cè)試的代碼進(jìn)行測(cè)試。就是說,要相信自己的感覺,自己的經(jīng)驗(yàn)。那些重要的功能、核心的代碼就應(yīng)該重點(diǎn)測(cè)試。感到疲勞就應(yīng)該停下來休息一下。感覺沒有必要更詳細(xì)的測(cè)試,就停止本輪測(cè)試。 測(cè)試驅(qū)動(dòng)開發(fā)強(qiáng)調(diào)測(cè)試并不應(yīng)該是負(fù)擔(dān),而應(yīng)該是幫助我們減輕工作量的方法。而對(duì)于何時(shí)停止編寫測(cè)試用例,也是應(yīng)該根據(jù)你的經(jīng)驗(yàn),功能復(fù)雜、核心功能的代碼就應(yīng)該編寫更全面、細(xì)致的測(cè)試用例,否則測(cè)試流程即可。 測(cè)試范圍沒有靜態(tài)的標(biāo)準(zhǔn),同時(shí)也應(yīng)該可以隨著時(shí)間改變。對(duì)于開始沒有編寫足夠的測(cè)試的功能代碼,隨著bug的出現(xiàn),根據(jù)bug補(bǔ)齊相關(guān)的測(cè)試用例即可。 小步前進(jìn)的原則,要求我們對(duì)大的功能塊測(cè)試時(shí),應(yīng)該先分拆成更小的功能塊進(jìn)行測(cè)試,比如一個(gè)類A使用了類B、C,就應(yīng)該編寫到A使用B、C功能的測(cè)試代碼前,完成對(duì)B、C的測(cè)試和開發(fā)。那么是不是每個(gè)小類或者小函數(shù)都應(yīng)該測(cè)試哪?我認(rèn)為沒有必要。你應(yīng)該運(yùn)用你的經(jīng)驗(yàn),對(duì)那些可能出問題的地方重點(diǎn)測(cè)試,感覺不可能出問題的地方就等它真正出問題的時(shí)候再補(bǔ)測(cè)試吧。 2. 怎么編寫測(cè)試用例 測(cè)試用例的編寫就用上了傳統(tǒng)的測(cè)試技術(shù)。 操作過程盡量模擬正常使用的過程。 八、Tips 很多朋友有疑問,“測(cè)試代碼的正確性如何保障?是寫測(cè)試代碼還是寫測(cè)試文檔?”這樣是不是會(huì)陷入“雞生蛋,蛋生雞”的循環(huán)。其實(shí)是不會(huì)的。通常測(cè)試代碼通常是非常簡(jiǎn)單的,通常圍繞著某個(gè)情況的正確性判斷的幾個(gè)語句,如果太復(fù)雜,就應(yīng)該繼續(xù)分解啦。而傳統(tǒng)的開發(fā)過程通常強(qiáng)調(diào)測(cè)試文檔。但隨著開發(fā)節(jié)奏的加快,用戶需求的不斷變化,維護(hù)高層(需求、概要設(shè)計(jì))的測(cè)試文檔可以,更低層的測(cè)試文檔的成本的確太大了。而且可實(shí)時(shí)驗(yàn)證功能正確性的測(cè)試代碼就是對(duì)代碼最好的文檔。 軟件開發(fā)過程中,除了遵守上面提到的測(cè)試驅(qū)動(dòng)開發(fā)的幾個(gè)原則外,一個(gè)需要注意的問題就是,謹(jǐn)防過度設(shè)計(jì)。編寫功能代碼時(shí)應(yīng)該關(guān)注于完成當(dāng)前功能點(diǎn),通過測(cè)試,使用最簡(jiǎn)單、直接的方式來編碼。過多的考慮后期的擴(kuò)展,其他功能的添加,無疑增加了過多的復(fù)雜性,容易產(chǎn)生問題。應(yīng)該等到要添加這些特性時(shí)在進(jìn)行詳細(xì)的測(cè)試驅(qū)動(dòng)開發(fā)。到時(shí)候,有整套測(cè)試用例做基礎(chǔ),通過不斷重構(gòu)很容易添加相關(guān)特性。 九、FAQ [什么時(shí)候重構(gòu)?] [什么時(shí)候設(shè)計(jì)?] [什么時(shí)候增加新的TestCase?] [TestCase該怎么寫?] [TDD能幫助我消除Bug嗎?] 但是,如果要問“測(cè)試”和“除蟲”之間有什么聯(lián)系,我相信還是有很多話可以講的,比如TDD事實(shí)上減少了bug的數(shù)量,把查找bug戰(zhàn)役的關(guān)注點(diǎn)從全線戰(zhàn)場(chǎng)提升到代碼戰(zhàn)場(chǎng)以上。還有,bug的最可怕之處不在于隱藏之深,而在于滿天遍野。如果你發(fā)現(xiàn)了一個(gè)用戶很不容易才能發(fā)現(xiàn)的bug,那么不一定對(duì)工作做出了什么杰出貢獻(xiàn),但是如果你發(fā)現(xiàn)一段代碼中,bug的密度或離散程度過高,那么恭喜你,你應(yīng)該拋棄并重寫這段代碼了。TDD避免了這種情況,所以將尋找bug的工作降低到了一個(gè)新的低度。 [我該為一個(gè)Feature編寫TestCase還是為一個(gè)類編寫TestCase?] 我們的研究結(jié)果表明,通常在一個(gè)特性的開發(fā)開始時(shí),我們針對(duì)特性編寫測(cè)試用例,如果您發(fā)現(xiàn)這個(gè)特性無法用TestCase表達(dá),那么請(qǐng)將這個(gè)特性細(xì)分,直至您可以為手上的特性寫出TestCase為止。從這里開始是最安全的,它不會(huì)導(dǎo)致任何設(shè)計(jì)上重大的失誤。但是,隨著您不斷的重構(gòu)代碼,不斷的重構(gòu)TestCase,不斷的依據(jù)TDD的思想做下去,最后當(dāng)產(chǎn)品伴隨測(cè)試用例集一起發(fā)布的時(shí)候,您就會(huì)不經(jīng)意的發(fā)現(xiàn)經(jīng)過重構(gòu)以后的測(cè)試用例很可能是和產(chǎn)品中的類/方法一一對(duì)應(yīng)的。 [什么時(shí)候應(yīng)該將全部測(cè)試都運(yùn)行一遍?] [什么時(shí)候改進(jìn)一個(gè)TestCase?] 但是,美國人的想法其實(shí)跟我們還是不太一樣,拿托尼巴贊的MindMap來說吧,其實(shí)畫MindMap只是為了表現(xiàn)自己的思路,或記憶某些重要的事情,但托尼卻建議大家把MindMap畫成一件藝術(shù)品,甚至還有很多藝術(shù)家把自己畫的抽象派MindMap拿出來幫助托尼做宣傳。同樣,大師們也要求我們把TestCase寫的跟代碼一樣質(zhì)量精良,可我想說的是,現(xiàn)在國內(nèi)有幾個(gè)公司能把產(chǎn)品的代碼寫的精良??還是一步一步慢慢來吧。 [為什么原來通過的測(cè)試用例現(xiàn)在不能通過了?] [我怎么知道那里該有一個(gè)方法還是該有一個(gè)類?] [我要寫一個(gè)TestCase,可是不知道從哪里開始?] [為什么我的測(cè)試總是看起來有點(diǎn)愚蠢?] [什么場(chǎng)合不適用TDD?] |
|
|