測試驅(qū)動開發(fā)

2002年出版的書中 Test Driven Development: By Example 就有系統(tǒng)提到了。
更早地,1998年日本的山浦恒雄就在 How to Design Practical Test Cases 論
文中就明確指出先寫測試用例可以極大降低軟件缺陷率。到達(dá)客戶端的軟件軟件
缺陷率是多少呢,0.02%。 這是什么概念? 假設(shè)一個軟件在它的生命周期中總
的缺陷是10000個, 到達(dá)用戶手中只有2個。 其它9998個都在出廠前就被發(fā)現(xiàn)并
且修復(fù)了。 可悲的是,國內(nèi)用TDD的公司幾乎沒有聽說過,“太高端了”,都在趕
進(jìn)度,憑本能在做事。
在常見的軟件開發(fā)過程中,我們把測試放在一個比較后面的階段去做。 典型的
比如瀑布模型。在瀑布模型中,開發(fā)遵循如下過程, 需求分析-設(shè)計(jì)-實(shí)現(xiàn)-測試-部
署與維護(hù)。一步一步,測試放在了第四步,放在編碼后面去做。 當(dāng)然,程序員
在開發(fā)的過程中,也會有一些不系統(tǒng)的、零散的測試。瀑布模型一個理想的模型,
瀑布之水,不會回流。 但是在實(shí)際開發(fā)中,我們一定是要回溯的。經(jīng)常地, 在
做后一階段的工作時,發(fā)現(xiàn)前一階段有些問題沒有考慮到,或沒有解決好,這時
不得不回溯到前面的階段。造成返工。按照瀑布模型開發(fā),客戶很晚才能看到軟
件。
嚴(yán)格遵循瀑布模型的項(xiàng)目幾乎沒有,也沒有必要。這里我說一個我比較喜歡的開
發(fā)過程。首先盡量全面準(zhǔn)確的收集客戶需求,做一個需求池,便于在設(shè)計(jì)時整體
考慮,做整體設(shè)計(jì)。需求階段會比較費(fèi)時費(fèi)力,目的是盡可能穩(wěn)定設(shè)計(jì)。其實(shí)這
就是瀑布模型的第一個階段, Requirements??傮w設(shè)計(jì)完成后,把池子里的需求
按照重要性大小排個序,把最核心的需求排在最前面,進(jìn)行編碼,先做好這個需
求,交付給客戶,讓客戶使用,盡早獲得反饋。這里其實(shí)是一個小型化的瀑布模
型。客戶反饋中很可能有需求變更,這時我們重新整理需求池子,對余下的需求
進(jìn)行排序,接著對第二核心的需求進(jìn)行編碼,交付給客戶,讓客戶使用,獲得反
饋。如此循環(huán),直到完成軟件開發(fā)完成。這種方法的特點(diǎn)是,測試提前了,交付
頻率高,客戶開發(fā)者互動多,最后的產(chǎn)品用戶滿意度高,返工的可能性小。這種
開發(fā)過程還不是測試驅(qū)動開發(fā)。
這是開發(fā)過程,下面看看開發(fā)費(fèi)用。 從餅狀圖我們可以看出,高質(zhì)量的嚴(yán)肅的
軟件,測試占了開發(fā)費(fèi)用了一半,而編碼只有1/6?,F(xiàn)在很多公司,編碼的花費(fèi)
很多,而測試的花費(fèi)很少。這不太好,除非你對自己的產(chǎn)品不負(fù)責(zé)任。? 這樣開
發(fā)出來的軟件可能質(zhì)量得不到保障,部署后遇到問題,花費(fèi)更大。右邊是冰山圖,
我們看到, 開發(fā)費(fèi)用只占軟件總體費(fèi)用的一小部分,維護(hù)費(fèi)用占據(jù)了大頭,是
冰山中沉在海面下的部分。Firefox, Ubuntu, Windows這些軟件的維護(hù)費(fèi)用就
是這個情況。
說到維護(hù),如果一個軟件的內(nèi)部質(zhì)量很差,維護(hù)起來是相當(dāng)困難,甚至不可能。
維護(hù)包括因需求變更而新增加功能。所以,如果一個軟件要長久維護(hù),開發(fā)之初,
不是快速功能點(diǎn)實(shí)現(xiàn)就行了,而是要提高軟件的內(nèi)部質(zhì)量,不這么做,急躁冒進(jìn),
以后想要修改軟件時,花費(fèi)更大。
越到后期,我們修復(fù)一個缺陷要花費(fèi)的代價就越大,代價是指數(shù)級增長的。軟件
的規(guī)模越大, 拖到后面修復(fù)缺點(diǎn)所需的代價就越大。圖中顯示的是Boehm給出真
實(shí)的數(shù)據(jù)。橫軸是開發(fā)階段,縱軸是修復(fù)缺陷的花費(fèi)(注意到縱軸是log-scale
的,對數(shù)標(biāo)尺)。這個圖告訴我們要盡早發(fā)現(xiàn)問題,否則代價大到難以承受。如
果某購物網(wǎng)站在某重要節(jié)假日忽然出現(xiàn)一個無法支付的軟件缺陷,而修復(fù)這個缺
陷花了4個小時,這樣的損失是非常大的。測試驅(qū)動開發(fā)有助于我們盡早發(fā)現(xiàn)問
題。要十分重視測試。 去店里買衣服,我們總要到試衣間試試看,合不合身。
軟件也一樣,無測試,不軟件。當(dāng)然,完全沒有測試的軟件公司是沒有的,關(guān)鍵
在于測試是不是做的充分,測試是不是做的聰明。盡量早的充分測試,有助與我
們增強(qiáng)對軟件的信心,即時發(fā)現(xiàn)問題、解決問題,降低風(fēng)險。在測試上做投資,
是很值得的,除非的你的軟件根本就不重要。
下面我們來看3個實(shí)際發(fā)生的例子。第一個出現(xiàn)在我們學(xué)生開發(fā)的作業(yè)提交軟件
中。大家仔細(xì)看這個加了黃框的文件路徑,發(fā)現(xiàn)什么潛在問題沒有?如果王莉同
時上兩門課,并且每門課都有Assignment2,并且王莉上傳的文件都叫
201631900112_王莉_A2.rar,這就會造成文件覆蓋。
第二個例子還是這個作業(yè)提交軟件,前幾天我想加一門新的課,叫做
Introduction to Object-oriented Design and Analysis,結(jié)果爆出了
Course_Name too long這樣的SQL錯誤。第三個例子是某個公司開發(fā)的某管理系
統(tǒng),兩個土地面積相加,出現(xiàn)的結(jié)果中,小數(shù)點(diǎn)后面出現(xiàn)了12位數(shù)字。這個系統(tǒng)
已經(jīng)完成用戶培訓(xùn)階段了。
這些問題,都是在獨(dú)立測試中發(fā)現(xiàn)的,我說的獨(dú)立測試是指程序員不參與、由第
三方做的測試。如果這些問題暴露在客戶面前,一方面會給他們造成不必要的麻
煩,或者“surprise”,他們麻煩了,就必然會來麻煩公司,另外一方面也是對公
司的信譽(yù)產(chǎn)生不良影響。
測試驅(qū)動開發(fā)怎么做?能幫助我們克服這些問題嗎? 測試驅(qū)動開發(fā), TDD, 要
“反直覺”, 要把這個測試工作盡量提前,提前到需求分析完成之后, 編寫代碼
之前,以至于編碼之前我就考慮種種測試用例,各種可能出現(xiàn)的情況了。 采取
測試驅(qū)動開發(fā)過程的團(tuán)隊(duì)怎么想,反正最后總要測試,把測試提前能不能幫助我
們更順利完成開發(fā)工作呢? 這個思想非常重要,非常有創(chuàng)新性。為什么呢?
就第一個例子來說,比如我在編碼之前就給出這樣一個測試用例:
王莉同學(xué)的學(xué)號是201631900112,她會同時選兩門課,A與B,每門課都有
Assignment2,并且每門課王莉上交的文件名都是201631900112_王莉_A2.rar。
有這樣的測試用例,就會逼迫我們?nèi)ピO(shè)計(jì)合理的文件存儲目錄結(jié)構(gòu)。
第二個例子,比如在編碼之前,我就設(shè)計(jì)如下三類的課程名:
空的課程名
長度是1-40的課程名
長度大于40的課程名
所以,測試驅(qū)動開發(fā)一個最重要的好處是讓我們在編程時考慮問題更全面,降低出現(xiàn)問題的可能性。
在測試驅(qū)動開發(fā)的框架下, 測試準(zhǔn)備工作從需求分析就要開始了。需求分析時,
每一個需求都是要可以被測試的,我們要為測試這個需求寫下測試用例。這里是
說寫下,不僅僅是想好。如果一個需求是含糊不清的,那么它就不是可以測試的。
可以測試的需求往往需要用數(shù)字表示,有具體的限定條件, “很快返回結(jié)果”,
很快是多快,不同人有不同的理解。
把測試工作盡量提前,最大好處的好處是“把較抽象的問題形象化,把較籠統(tǒng)的
問題具體化,把較模糊的問題清晰化”。 既然已經(jīng)決定要先寫測試用例了,下面
我們要決定測試用例的密度,按照日本日立公司山浦恒雄的經(jīng)驗(yàn),10行代碼配套
1個測試用例就會有很低的客戶端缺陷發(fā)生率。記住,是10:1。 1萬行代碼的軟
件,需要配套1000個測試用例。這已經(jīng)很多了,是不是這是日本東西質(zhì)量高的一
個重要原因。 有的人說,可不可以是5:1,可不可以使15:1,當(dāng)然是可以的,
看你對軟件復(fù)雜度與你對軟件可靠性的需求。? 10:1這樣一個比例其實(shí)是提供
了一個目標(biāo),有了目標(biāo)就可以操作。
山浦恒雄列舉了測試驅(qū)動開發(fā)的5大優(yōu)點(diǎn)。
最后,我給大家講一個用Python語言實(shí)踐TDD的簡單例子。 我在寫這個測試腳本
用來測試remove_punctuation(在字符串中移除標(biāo)點(diǎn)符號)的時候,
remove_punctuation這個函數(shù)還沒有實(shí)現(xiàn)。所以,一開始運(yùn)行這個程序,基本上
是沒有測試用例通過的,屏幕一片紅色。但是這些測試用例給出了非常具體的輸
入與輸出,在我設(shè)計(jì)程序時起到一個參謀的作用,使得我考慮問題更加全面。我
考慮了一般情況, 也考慮了異常情況(Undesired Events)。 比如輸入是空字符
串,或是None,輸出的結(jié)果是什么,都是清楚的。好處就在于明確,可操作,并
且給人一種“游戲通關(guān)”的刺激感覺。屏幕由紅變綠,最后全綠,給人成就感。最
后,這個測試腳本是可以被重復(fù)利用的,可以做回歸測試, 可以請別人去執(zhí)行,
可以晚上我們睡覺時執(zhí)行,等等優(yōu)點(diǎn)。所以說,測試用例是有價值的投資,投資
的回報(bào)就是軟件的質(zhì)量提高、公司形象提升、員工成就感提升。
測試驅(qū)動開發(fā)的一個最大障礙就是急躁心理。欲速則不達(dá)。
Projects that aim from the beginning at achieving the shortest
possible schedules regardless of quality considerations, tend to have
the fairly high frequencies of both schedule and cost
overruns. Software projects that aim initially at achieving the
highest possible levels of quality and reliability tend to have the
best schedule adherence records, the highest productivity, and even
the best marketplace success. - Capers /'keip?z/ Jones
TDD是不是好方法?你在實(shí)踐中用TDD方法嗎?歡迎與我討論。我的微博名字就是
藍(lán)琿。
標(biāo)簽: