單元測(cè)試
單元測(cè)試(unit testing),是指對(duì)軟件中的最小可測(cè)試單元進(jìn)行檢查和驗(yàn)證。對(duì)于單元測(cè)試中單元的含義,一般來說,要根據(jù)實(shí)際情況去判定其具體含義,如C語(yǔ)言中單元指一個(gè)函數(shù),Java里單元指一個(gè)類,圖形化的軟件中可以指一個(gè)窗口或一個(gè)菜單等。總的來說,單元就是人為規(guī)定的最小的被測(cè)功能模塊。單元測(cè)試是在軟件開發(fā)過程中要進(jìn)行的最低級(jí)別的測(cè)試活動(dòng),軟件的獨(dú)立單元將在與程序的其他部分相隔離的情況下進(jìn)行測(cè)試。
在一種傳統(tǒng)的結(jié)構(gòu)化編程語(yǔ)言中,比如C,要進(jìn)行測(cè)試的單元一般是函數(shù)或子過程。在像C++這樣的面向?qū)ο蟮恼Z(yǔ)言中, 要進(jìn)行測(cè)試的基本單元是類。對(duì)Ada語(yǔ)言來說,開發(fā)人員可以選擇是在獨(dú)立的過程和函數(shù),還是在Ada包的級(jí)別上進(jìn)行單元測(cè)試。單元測(cè)試的原則同樣被擴(kuò)展到第四代語(yǔ)言(4GL)的開發(fā)中,在這里基本單元被典型地劃分為一個(gè)菜單或顯示界面。
經(jīng)常與單元測(cè)試聯(lián)系起來的另外一些開發(fā)活動(dòng)包括代碼走讀(Code review),靜態(tài)分析(Static analysis)和動(dòng)態(tài)分析(Dynamic analysis)。靜態(tài)分析就是對(duì)軟件的源代碼進(jìn)行研讀,查找錯(cuò)誤或收集一些度量數(shù)據(jù),并不需要對(duì)代碼進(jìn)行編譯和執(zhí)行。動(dòng)態(tài)分析就是通過觀察軟件運(yùn)行時(shí)的動(dòng)作,來提供執(zhí)行跟蹤,時(shí)間分析,以及測(cè)試覆蓋度方面的信息。
單元測(cè)試實(shí)施要點(diǎn)
1. 模塊接口
模塊的接口保證了測(cè)試模塊的數(shù)據(jù)流可以正確地流人、流出。在測(cè)試中應(yīng)檢查以下要點(diǎn):
1) 測(cè)試模塊的輸入參數(shù)和形式參數(shù)在個(gè)數(shù)、屬性、單位上是否一致。
2) 調(diào)用其他模塊時(shí)所給出的實(shí)際參數(shù)和被調(diào)用模塊的形式參數(shù)在個(gè)數(shù)、屬性、單位上是否一致。
3) 調(diào)用標(biāo)準(zhǔn)函數(shù)時(shí)所用的參數(shù)在屬性、數(shù)目和順序上是否正確。
4) 全局變量在各模塊中的定義和用法是否一致。
5) 輸入是否僅改變了形式參數(shù)。
6) 開/關(guān)的語(yǔ)句是否正確。
7) 規(guī)定的I/O格式是否與輸入輸出語(yǔ)句一致。
8) 在使用文件之前是否已經(jīng)打開文件或是使用文件之后是否已經(jīng)關(guān)閉文件。
2. 局部數(shù)據(jù)結(jié)構(gòu)。
在單元測(cè)試中,局部數(shù)據(jù)結(jié)構(gòu)出錯(cuò)是比較常見的錯(cuò)誤,在測(cè)試剛應(yīng)重點(diǎn)考慮以下因素:
1) 變量的說明是否合適。
2) 是否使用了尚未賦值或尚未初始化的變量。 3) 變量的初始值或默認(rèn)值是否正確。 4) 變量名是否有錯(cuò)(例如拼寫錯(cuò))。
3. 重要的執(zhí)行路徑。
在單元測(cè)試中,對(duì)路徑的測(cè)試是最基本的任務(wù)。由于不能進(jìn)行窮舉測(cè)試,需要精心設(shè)計(jì)測(cè)試用例來發(fā)現(xiàn)是否有計(jì)算、比較或控制流等方面的錯(cuò)誤。
1) 計(jì)算方面的錯(cuò)誤:算術(shù)運(yùn)算的優(yōu)先次序不正確或理解錯(cuò)誤;精度不夠;運(yùn)算對(duì)象的
類型不匹配;算法錯(cuò);表達(dá)式的符號(hào)表示不正確等。
2) 比較和控制流的錯(cuò)誤:本應(yīng)相等的量由于精度造成不相等;不同類型進(jìn)行比較邏輯
運(yùn)算符不正確或優(yōu)先次序錯(cuò)誤;循環(huán)終止不正確(如多循環(huán)一次或少循環(huán)一次)、死循環(huán);不恰當(dāng)?shù)匦薷难h(huán)變量;當(dāng)遇到分支循環(huán)時(shí),出口錯(cuò)誤等。
4. 出錯(cuò)處理。
好的設(shè)計(jì)應(yīng)該能預(yù)測(cè)到出錯(cuò)的條件并且有出錯(cuò)處理的途徑。雖然計(jì)算機(jī)機(jī)可以顯示出錯(cuò)信息的內(nèi)容,但仍需要程序員對(duì)出錯(cuò)進(jìn)行處理,保證其邏輯的正確性以便于用戶維護(hù)。
5. 邊界條件
邊界條件的測(cè)試是單元測(cè)試的最后工作,也是非常重要的工作。毫件容易在邊界出現(xiàn)錯(cuò)誤。塊進(jìn)行測(cè)試時(shí),需要開發(fā)兩種模塊:
6. 驅(qū)動(dòng)模塊
相當(dāng)于一個(gè)主程序,接收測(cè)試用例的數(shù)據(jù),將這些數(shù)據(jù)送到測(cè)試槨,輸出測(cè)試結(jié)果。
7. 樁模塊
也稱為存根模塊。樁模塊用來代替測(cè)試模塊中所調(diào)用的子模塊,其進(jìn)行少量的數(shù)據(jù)處理,目的是為了檢驗(yàn)人口,輸出調(diào)用和返回的信息。 提高模塊的內(nèi)聚度可以簡(jiǎn)化單元測(cè)試。如果每個(gè)模塊只完成一種功能,對(duì)于具一塊來講,所需的測(cè)試方案數(shù)據(jù)就會(huì)顯著減少,而且更容易發(fā)現(xiàn)和預(yù)測(cè)模塊中的錯(cuò)誤。
單元測(cè)試經(jīng)驗(yàn)總結(jié)
1. 概述
工廠在組裝一臺(tái)電視機(jī)之前,會(huì)對(duì)每個(gè)元件都進(jìn)行測(cè)試,這,就是單元測(cè)試。其實(shí)我們每天都在做單元測(cè)試。你寫了一個(gè)函數(shù),除了極簡(jiǎn)單的外,總是要執(zhí)行一下,看看功能是否正常,有時(shí)還要想辦法輸出些數(shù)據(jù),如彈出信息窗口什么的,這,也是單元測(cè)試,我們把這種單元測(cè)試稱為臨時(shí)單元測(cè)試。只進(jìn)行了臨時(shí)單元測(cè)試的軟件,針對(duì)代碼的測(cè)試很不完整,代碼覆蓋率要超過70%都很困難,未覆蓋的代碼可能遺留大量的細(xì)小的錯(cuò)誤,這些錯(cuò)誤還會(huì)互相影響,當(dāng)BUG暴露出來的時(shí)候難于調(diào)試,大幅度提高后期測(cè)試和維護(hù)成本,也降低了開發(fā)商的競(jìng)爭(zhēng)力??梢哉f,進(jìn)行充分的單元測(cè)試,是提高軟件質(zhì)量,降低開發(fā)成本的必由之路。對(duì)于程序員來說,如果養(yǎng)成了對(duì)自己寫的代碼進(jìn)行單元測(cè)試的習(xí)慣,不但可以寫出高質(zhì)量的代碼,而且還能提高編程水平。
要進(jìn)行充分的單元測(cè)試,應(yīng)專門編寫測(cè)試代碼,并與產(chǎn)品代碼隔離。我們認(rèn)為,比較簡(jiǎn)單的辦法是為產(chǎn)品工程建立對(duì)應(yīng)的測(cè)試工程,為每個(gè)類建立對(duì)應(yīng)的測(cè)試類,為每個(gè)函數(shù)(很簡(jiǎn)單的除外)建立測(cè)試函數(shù)。首先就幾個(gè)概念談?wù)勎覀兊目捶ā?/p>
一般認(rèn)為,在結(jié)構(gòu)化程序時(shí)代,單元測(cè)試所說的單元是指函數(shù),在當(dāng)今的面向?qū)ο髸r(shí)代,單元測(cè)試所說的單元是指類。以我們的實(shí)踐來看,以類作為測(cè)試單位,復(fù)雜度高,可操作性較差,因此仍然主張以函數(shù)作為單元測(cè)試的測(cè)試單位,但可以用一個(gè)測(cè)試類來組織某個(gè)類的所有測(cè)試函數(shù)。單元測(cè)試不應(yīng)過分強(qiáng)調(diào)面向?qū)ο?,因?yàn)榫植看a依然是結(jié)構(gòu)化的。單元測(cè)試的工作量較大,簡(jiǎn)單實(shí)用高效才是硬道理。
有一種看法是,只測(cè)試類的接口(公有函數(shù)),不測(cè)試其他函數(shù),從面向?qū)ο蠼嵌葋砜矗_實(shí)有其道理,但是,測(cè)試的目的是找錯(cuò)并最終排錯(cuò),因此,只要是包含錯(cuò)誤的可能性較大的函數(shù)都要測(cè)試,跟函數(shù)是否私有沒有關(guān)系。對(duì)于C++來說,可以用一種簡(jiǎn)單的方法區(qū)隔需測(cè)試的函數(shù):簡(jiǎn)單的函數(shù)如數(shù)據(jù)讀寫函數(shù)的實(shí)現(xiàn)在頭文件中編寫(inline函數(shù)),所有在源文件編寫實(shí)現(xiàn)的函數(shù)都要進(jìn)行測(cè)試(構(gòu)造函數(shù)和析構(gòu)函數(shù)除外)。
2.什么時(shí)間開始測(cè)試
什么時(shí)候測(cè)試?單元測(cè)試越早越好,早到什么程度?XP開發(fā)理論講究TDD,即測(cè)試驅(qū)動(dòng)開發(fā),先編寫測(cè)試代碼,再進(jìn)行開發(fā)。在實(shí)際的工作中,可以不必過分強(qiáng)調(diào)先什么后什么,重要的是高效和感覺舒適。從我們的經(jīng)驗(yàn)來看,先編寫產(chǎn)品函數(shù)的框架,然后編寫測(cè)試函數(shù),針對(duì)產(chǎn)品函數(shù)的功能編寫測(cè)試用例,然后編寫產(chǎn)品函數(shù)的代碼,每寫一個(gè)功能點(diǎn)都運(yùn)行測(cè)試,隨時(shí)補(bǔ)充測(cè)試用例。所謂先編寫產(chǎn)品函數(shù)的框架,是指先編寫函數(shù)空的實(shí)現(xiàn),有返回值的隨便返回一個(gè)值,編譯通過后再編寫測(cè)試代碼,這時(shí),函數(shù)名、參數(shù)表、返回類型都應(yīng)該確定下來了,所編寫的測(cè)試代碼以后需修改的可能性比較小。
3.誰(shuí)來測(cè)試
由誰(shuí)測(cè)試?單元測(cè)試與其他測(cè)試不同,單元測(cè)試可看作是編碼工作的一部分,應(yīng)該由程序員完成,也就是說,經(jīng)過了單元測(cè)試的代碼才是已完成的代碼,提交產(chǎn)品代碼時(shí)也要同時(shí)提交測(cè)試代碼。測(cè)試部門可以作一定程度的審核。
4.關(guān)于樁代碼
我們認(rèn)為,單元測(cè)試應(yīng)避免編寫樁代碼。樁代碼就是用來代替某些代碼的代碼,例如,產(chǎn)品函數(shù)或測(cè)試函數(shù)調(diào)用了一個(gè)未編寫的函數(shù),可以編寫樁函數(shù)來代替該被調(diào)用的函數(shù),樁代碼也用于實(shí)現(xiàn)測(cè)試隔離。采用由底向上的方式進(jìn)行開發(fā),底層的代碼先開發(fā)并先測(cè)試,可以避免編寫樁代碼,這樣做的好處有:減少了工作量;測(cè)試上層函數(shù)時(shí),也是對(duì)下層函數(shù)的間接測(cè)試;當(dāng)下層函數(shù)修改時(shí),通過回歸測(cè)試可以確認(rèn)修改是否導(dǎo)致上層函數(shù)產(chǎn)生錯(cuò)誤。
單元測(cè)試的基本策略
1.概述
當(dāng)設(shè)計(jì)一個(gè)單元測(cè)試的策略時(shí),可以采用三種基本的組織方法。它們分別是自上而下法、自下而上法和分離法。在接下來的第二、第三和第四部分將對(duì)上述三種方法的詳細(xì)內(nèi)容、各自的優(yōu)點(diǎn)和缺點(diǎn)分別進(jìn)行介紹。在文章中要一直用到測(cè)試驅(qū)動(dòng)和樁模塊這兩個(gè)概念。所謂的測(cè)試驅(qū)動(dòng)是指能使軟件執(zhí)行的軟件,它的目的就是為了測(cè)試軟件,提供一個(gè)能設(shè)置輸入?yún)?shù)的框架,并執(zhí)行這個(gè)框架單元以得到相應(yīng)的輸出參數(shù)。而樁模塊是指一個(gè)模擬單元,用這個(gè)模擬單元來替代真實(shí)的單元完成測(cè)試。
2. 自上而下法 2.1 詳述
在自上而下的測(cè)試過程中,每個(gè)單元是通過使用它們來進(jìn)行測(cè)試的,這個(gè)過程是由調(diào)用這些被測(cè)單元的其他獨(dú)立的單元完成的。
首先測(cè)試最高層的單元,將所有的調(diào)用單元用樁模塊替換。接著用實(shí)際的調(diào)用單元替換樁模塊,而繼續(xù)將較低層次的單元用樁模塊替換。重復(fù)這個(gè)過程直到測(cè)試了最底層的單元。自上而下測(cè)試法需要測(cè)試樁,而不需要測(cè)試驅(qū)動(dòng)。
圖2.1描述了使用測(cè)試樁和一些已測(cè)試單元來測(cè)試單元D的過程,假設(shè)單元A,B,C已經(jīng)用自上而下法進(jìn)行了測(cè)試。
由圖2.1得到的是一個(gè)使用基于自上而下組織方法的單元測(cè)試計(jì)劃,其過程可以描述如下:
1) 步驟1:測(cè)試A單元,使用B,C,D單元的樁模塊。
2) 步驟2:測(cè)試B單元,通過已測(cè)試過的A單元來調(diào)用它,并且使用C,D單元的樁
模塊。步驟3:測(cè)試C單元,通過已測(cè)試過的A單元來調(diào)用它,并且使用已通過測(cè)試的B單元和D單元的樁模塊。
3) 步驟4:測(cè)試D單元,從已測(cè)試過的A單元調(diào)用它,使用已測(cè)試過的B和C單元,
并且將E,F(xiàn)和G單元用樁模塊代替。(如圖2.1所示)
4) 步驟5:測(cè)試E單元,通過已測(cè)試過的D單元調(diào)用它,而D單元是由已通過測(cè)試
的A單元來調(diào)用的,使用已通過測(cè)試的B和C單元,并且將F,G,H,I和J單元用樁模塊代替。
5) 步驟6:測(cè)試F單元,通過已測(cè)試過的D單元調(diào)用它,而D單元是由已通過測(cè)試
的A單元來調(diào)用的,使用已通過測(cè)試的B,C和E單元,并且將G,H,I和J單元用樁模塊代替。
6) 步驟7:測(cè)試G單元,通過已測(cè)試過的D單元調(diào)用它,而D單元是由已通過測(cè)試
的A單元來調(diào)用的,使用已通過測(cè)試的B,C和F單元,并且將H,I和J單元用樁模塊代替。
7) 步驟8:測(cè)試H單元,通過已測(cè)試過的E單元調(diào)用它,而E單元是由已通過測(cè)試的D單元來調(diào)用的,而D單元是由已通過測(cè)試的A單元來調(diào)用的,使用已通過測(cè)試的B,C,E,F(xiàn),G和H單元,并且將J單元用樁模塊代替。
8) 步驟9:測(cè)試J單元,通過已測(cè)試過的E單元調(diào)用它,而E單元是由已通過測(cè)試的
D單元來調(diào)用的,而D單元是由已通過測(cè)試的A單元來調(diào)用的,使用已通過測(cè)試的B,C,E,F(xiàn),G,H和I單元
2.2 優(yōu)點(diǎn)
自上而下單元測(cè)試法提供了一種軟件集成階段之前的較早的單元集成方法。實(shí)際上,自上而下單元測(cè)試法確實(shí)將單元測(cè)試和軟件集成策略進(jìn)行了組合。
單元的詳細(xì)設(shè)計(jì)是自上而下的,自上而下的測(cè)試實(shí)現(xiàn)過程使得被測(cè)單元按照原設(shè)計(jì)的順序進(jìn)行,因?yàn)閱卧獪y(cè)試的詳細(xì)設(shè)計(jì)與軟件生命周期代碼設(shè)計(jì)階段的重疊,所以開發(fā)時(shí)間將被縮短。
在通常的結(jié)構(gòu)化設(shè)計(jì)中,高等級(jí)的單元提供高層的功能,而低等級(jí)的單元實(shí)現(xiàn)細(xì)節(jié),自上而下的單元測(cè)試將提供一種早期的“可見”的功能化集成。它給予單元測(cè)試一種必要的合理的實(shí)現(xiàn)途徑。
較低層次的多余功能可以通過自上而下法來鑒別,這是因?yàn)闆]有路徑來測(cè)試它。(但是,這可能在區(qū)分多余的功能和沒有被測(cè)試的功能時(shí)帶來困難)。
2.3 缺點(diǎn)
自上而下法是通過樁模塊來進(jìn)行控制的,而且測(cè)試用例常常涉及很多的樁模塊。對(duì)于每個(gè)已測(cè)單元來說,測(cè)試變得越來越復(fù)雜,結(jié)果是開發(fā)和維護(hù)的費(fèi)用也越來越昂貴。
依層次進(jìn)行的自上而下的測(cè)試,要達(dá)到一個(gè)好的覆蓋結(jié)構(gòu)也很困難,而這對(duì)于一個(gè)較為完善、安全的關(guān)鍵性應(yīng)用來說至為重要,同時(shí)這也是很多的標(biāo)準(zhǔn)所要求的。難于達(dá)到一個(gè)好的覆蓋結(jié)構(gòu)也可能導(dǎo)致最終的多余功能和未測(cè)試功能之間的混亂。由此,測(cè)試一些低層次的功能,特別是錯(cuò)誤處理代碼,將徹底不切實(shí)。
一個(gè)單元的變化往往會(huì)影響對(duì)其兄弟單元和下層單元的測(cè)試。例如,考慮一下D單元一個(gè)變化。很明顯,對(duì)D單元的單元測(cè)試不得不發(fā)生變化和重新進(jìn)行。另外,要使用已測(cè)試單元D的E、F、G、H、I和J單元也不得不重新測(cè)試。作為單元D改變的結(jié)果,上述測(cè)試自身可能也不得不發(fā)生改變,即使單元E、F、G、H、I和J實(shí)際上并沒有改變。這將導(dǎo)致當(dāng)變化發(fā)生時(shí),重復(fù)測(cè)試帶來的高成本,以及高額的維護(hù)成本和高額的整個(gè)軟件生產(chǎn)周期的成本。
在為自上而下測(cè)試法設(shè)計(jì)測(cè)試用例當(dāng)中,當(dāng)被測(cè)單元調(diào)用其他單元時(shí)需要測(cè)試人員具備結(jié)構(gòu)化知識(shí)。被測(cè)試單元的順序受限于單元的層次結(jié)構(gòu),低層次的單元必須要等到高層次的單元被測(cè)試后才能被測(cè)試,這樣就形成了一個(gè)“又長(zhǎng)又瘦”的單元測(cè)試階段。(然而,這可能會(huì)導(dǎo)致測(cè)試詳細(xì)設(shè)計(jì)與軟件生命周期編碼階段的整體重疊。)
如圖2.1所示的例子程序中各個(gè)單元之間的層次關(guān)系十分簡(jiǎn)單,在實(shí)際的編程過程中可能會(huì)遇到類似的情形,而且各個(gè)單元之間的層次關(guān)系會(huì)更復(fù)雜。所以自上而下測(cè)試法的缺點(diǎn)對(duì)單元測(cè)試造成的不利影響會(huì)隨著被測(cè)單元之間復(fù)雜的聯(lián)系而加深。
2.4 總結(jié)
一個(gè)自上而下的測(cè)試策略成本將高于基于分離的測(cè)試策略,這取決于頂層單元下層單元的復(fù)雜程度,以及由于下層單元自身發(fā)生變化所帶來的顯著影響。對(duì)于單元測(cè)試來說自上而下的組織方法不是一個(gè)好的選擇。然而,當(dāng)各個(gè)組成單元已經(jīng)被單獨(dú)測(cè)試的情況下,用自上而下法進(jìn)行單元的集成測(cè)試是個(gè)不錯(cuò)的手段。
3. 自下而上法 3.1 詳述
在自下而上的單元測(cè)試中,被測(cè)單元與調(diào)用被測(cè)單元的單元是分開測(cè)試的,但是測(cè)試時(shí)所使用的是真實(shí)的被調(diào)用單元。
測(cè)試時(shí)最底層的單元首先被測(cè)試,這樣就方便了對(duì)高層次單元的測(cè)試。然后使用前面已經(jīng)被測(cè)試過的被調(diào)用單元來測(cè)試其他的單元。重復(fù)這個(gè)過程直到最高層的單元被測(cè)試為止。自下而上法需要測(cè)試驅(qū)動(dòng),但是不需要測(cè)試樁。
圖3.1說明了測(cè)試D單元時(shí)需要的測(cè)試驅(qū)動(dòng)和已測(cè)單元的情況,假設(shè)單元E、F、G、H、I和J已經(jīng)通過自下而上法進(jìn)行了測(cè)試。
圖3.1顯示了一個(gè)程序的單元測(cè)試的測(cè)試計(jì)劃,該計(jì)劃使用了基于自下而上的組織方法,其過程如下: 步驟(1)(注意在測(cè)試步驟中測(cè)試的順序不是最主要的,步驟1中的所有測(cè)試可以同步進(jìn)行)。
1) 測(cè)試單元H,在調(diào)用H單元的E單元處使用一個(gè)測(cè)試驅(qū)動(dòng); 2) 測(cè)試單元I,在調(diào)用I單元的E單元處使用一個(gè)測(cè)試驅(qū)動(dòng); 3) 測(cè)試單元J,在調(diào)用J單元的E單元處使用一個(gè)測(cè)試驅(qū)動(dòng); 4) 測(cè)試單元F,在調(diào)用F單元的D單元處使用一個(gè)測(cè)試驅(qū)動(dòng); 5) 測(cè)試單元G,在調(diào)用G單元的D單元處使用一個(gè)測(cè)試驅(qū)動(dòng); 6) 測(cè)試單元B,在調(diào)用B單元的A單元處使用一個(gè)測(cè)試驅(qū)動(dòng); 7) 測(cè)試單元C,在調(diào)用C單元的A單元處使用一個(gè)測(cè)試驅(qū)動(dòng)。 步驟(2)
測(cè)試單元E,在調(diào)用E單元的D單元處使用一個(gè)測(cè)試驅(qū)動(dòng),再加上已測(cè)試過的單元H、I和J。 步驟(3)
測(cè)試單元D,在調(diào)用D單元的A單元處使用一個(gè)測(cè)試驅(qū)動(dòng),再加上已測(cè)試過的單元E、F、G、H、I和J。(如圖3.1所示) 步驟(4)
測(cè)試單元A,使用已測(cè)試過的單元B、C、D、E、F、G、H、I和J。
3.2 優(yōu)點(diǎn)
和自上而下法一樣,自下而上單元測(cè)試法提供了一種比軟件集成階段更早的單元集成。自下而上單元測(cè)試同樣也是真正意義上的單元測(cè)試和軟件集成策略的結(jié)合。因?yàn)椴恍枰獪y(cè)試樁,所以所有的測(cè)試用例都由測(cè)試驅(qū)動(dòng)控制。這樣就使得低層次單元附近的單元測(cè)試相對(duì)簡(jiǎn)單些。(但是,高層次單元的測(cè)試可能會(huì)變得很復(fù)雜。)
在使用自下而上法測(cè)試時(shí),測(cè)試用例的編寫可能只需要功能性的設(shè)計(jì)信息,不需要結(jié)構(gòu)化的設(shè)計(jì)信息(盡管結(jié)構(gòu)化設(shè)計(jì)信息可能有利于實(shí)現(xiàn)測(cè)試的全覆蓋)。所以當(dāng)詳細(xì)的設(shè)計(jì)文檔缺乏結(jié)構(gòu)化的細(xì)節(jié)時(shí),自下而上的單元測(cè)試就變得十分有用處。
自下而上單元測(cè)試法提供了一種低層次功能性的集成,而較高層次的功能隨著單元測(cè)試過程的進(jìn)行按照單元層次關(guān)系逐層增加。這就使得自下而上單元測(cè)試很容易地與測(cè)試對(duì)象相兼容。
3.3 缺點(diǎn)
隨著測(cè)試逐層推進(jìn),自下而上單元測(cè)試變得越來越復(fù)雜,隨之而來的是開發(fā)和維護(hù)的成本越來越高昂,同樣要實(shí)現(xiàn)好的結(jié)構(gòu)覆蓋也變得越來越困難。
低層單元的變化經(jīng)常影響其上層單元的測(cè)試。例如:想象一下H單元發(fā)生變化的情況。很明顯,對(duì)H單元的測(cè)試不得不發(fā)生變化和重新進(jìn)行。另外,對(duì)于A、D和E單元的測(cè)試來說,因?yàn)樗鼈児餐褂昧艘褱y(cè)試過的H單元,所以它們的測(cè)試也不得不重做。作為H單元發(fā)生變化的后果,這些測(cè)試本身可能也要進(jìn)行改變,即使單元A、D和E實(shí)際上并沒有發(fā)生變化。這就導(dǎo)致了當(dāng)變化發(fā)生時(shí),產(chǎn)生了與重新測(cè)試有關(guān)的高額代價(jià),以及高額的維護(hù)成本和整個(gè)軟件生命周期成本的提高。
單元測(cè)試的順序取決于單元的層次關(guān)系,較高層次的單元必須要等到較低層次單元通過測(cè)試后才能進(jìn)行測(cè)試,所以就形成了“長(zhǎng)瘦”型的單元測(cè)試階段。最先被測(cè)試的單元是最后被設(shè)計(jì)的單元,所以單元測(cè)試不能與軟件生命周期的詳細(xì)設(shè)計(jì)階段重疊。
如圖2.2所示的例子程序中各個(gè)單元之間的層次關(guān)系十分簡(jiǎn)單,在實(shí)際的編程過程中可能會(huì)遇到類似的情形,而且各個(gè)單元之間的層次關(guān)系會(huì)更復(fù)雜。與自上而下測(cè)試法一樣,自下而上測(cè)試法的缺點(diǎn)會(huì)隨著被測(cè)單元之間復(fù)雜的聯(lián)系而放大。
3.4 總結(jié)
自下而上組織法對(duì)于單元測(cè)試來說是個(gè)比較好的手段,特別是當(dāng)測(cè)試對(duì)象和重用情況時(shí)。然而,自下而上方法偏向于功能性測(cè)試,而不是結(jié)構(gòu)化測(cè)試。對(duì)于很多標(biāo)準(zhǔn)所需要的高集成度和安全的關(guān)鍵性應(yīng)用,需要達(dá)到高層次的結(jié)構(gòu)覆蓋,但自下而上法很難滿足這個(gè)要求。
自下而上單元測(cè)試法與很多軟件開發(fā)所要求的緊湊的時(shí)間計(jì)劃是相沖突的。總的來說,一個(gè)自下而上策略成本將高于基于分離的測(cè)試策略,這是因?yàn)閱卧獙哟谓Y(jié)構(gòu)中低層次單元以上單元的復(fù)雜程度和它們發(fā)生變化所帶來的顯著影響。
4. 分離法 4.1 詳述
分離測(cè)試法是分開測(cè)試每一個(gè)單元,無論是被調(diào)用單元還是調(diào)用單元。被測(cè)單元可以按照任意順序進(jìn)行測(cè)試,因?yàn)楸粶y(cè)單元不需要其他任何已測(cè)單元的支持。每一個(gè)單元的測(cè)試都需要一個(gè)測(cè)試驅(qū)動(dòng),并且所有的被調(diào)用單元都要用測(cè)試樁代替。圖4.1 說明了測(cè)試單元D 時(shí)需要的測(cè)試驅(qū)動(dòng)和測(cè)試樁的情況。
圖4.1 顯示了某個(gè)程序中一個(gè)單元的測(cè)試計(jì)劃,該計(jì)劃基于分離組織方法的策略,只需要如下所示的一步:
步驟(1)(注意該測(cè)試計(jì)劃只有一步。測(cè)試的順序不是最主要的,所有的測(cè)試可以同步進(jìn)行。)
1) 測(cè)試A 單元,使用一個(gè)測(cè)試驅(qū)動(dòng)啟動(dòng)測(cè)試,并且將B、C 和D 單元換成測(cè)試樁; 2) 測(cè)試B 單元,在A 單元處使用一個(gè)測(cè)試驅(qū)動(dòng)來調(diào)用B 單元; 3) 測(cè)試C 單元,在A 單元處使用一個(gè)測(cè)試驅(qū)動(dòng)來調(diào)用C 單元;
4) 測(cè)試D 單元,在A 單元處使用一個(gè)測(cè)試驅(qū)動(dòng)來調(diào)用D 單元,并且將E、F和G
單元換成測(cè)試樁(如圖3.1 所示);
5) 測(cè)試E 單元,在D 單元處使用一個(gè)測(cè)試驅(qū)動(dòng)來調(diào)用E 單元,并且將H、I和J
單元換成測(cè)試樁;
6) 測(cè)試F 單元,在D 單元處使用一個(gè)測(cè)試驅(qū)動(dòng)來調(diào)用F 單元; 7) 測(cè)試G 單元,在D 單元處使用一個(gè)測(cè)試驅(qū)動(dòng)來調(diào)用G 單元; 8) 測(cè)試H 單元,在E 單元處使用一個(gè)測(cè)試驅(qū)動(dòng)來調(diào)用H 單元; 9) 測(cè)試I 單元,在E 單元處使用一個(gè)測(cè)試驅(qū)動(dòng)來調(diào)用I 單元; 10) 測(cè)試J 單元,在E 單元處使用一個(gè)測(cè)試驅(qū)動(dòng)來調(diào)用J 單元。
4.2 優(yōu)點(diǎn)
徹底地測(cè)試一個(gè)分離的單元是很容易做到的,單元測(cè)試將其從與其它單元之間復(fù)雜的關(guān)系中分離了出來。分離測(cè)試是最容易實(shí)現(xiàn)良好的結(jié)構(gòu)性覆蓋的方法,并且實(shí)現(xiàn)良好結(jié)構(gòu)性覆蓋的困難程度與確定某一個(gè)單元在單元層次中所處位置的難易度沒有什么不同。
因?yàn)槊恳淮沃粶y(cè)試一個(gè)單元,所以該方法中所使用的測(cè)試驅(qū)動(dòng)比自下而上法中所使用的測(cè)試驅(qū)動(dòng)簡(jiǎn)單,該方法中所使用的測(cè)試樁比自上而下法中使用的測(cè)試樁簡(jiǎn)單。由于采用了分離的方法進(jìn)行單元測(cè)試,被測(cè)單元之間沒有依賴關(guān)系,所以單元測(cè)試階段可以和詳細(xì)設(shè)計(jì)階段,以及軟件生命周期的代碼編寫階段重疊。所有單元都能同步測(cè)試,形成了單元測(cè)試階段“短而寬”的特點(diǎn)。這有利于通過擴(kuò)大團(tuán)隊(duì)規(guī)模的手段縮短整個(gè)軟件開發(fā)的時(shí)間。分離測(cè)試法另外一個(gè)優(yōu)點(diǎn)是去除了測(cè)試單元之間的內(nèi)部依賴關(guān)系,所以當(dāng)一個(gè)單元發(fā)生變化時(shí)只需要改變那個(gè)發(fā)生變化的測(cè)試單元,而對(duì)其它測(cè)試單元沒有任何影響。由此可以看出分離組織法的成本要低于自下而上組織法和自上而下組織法,特別是當(dāng)發(fā)生變化時(shí)其效果更加明顯。
分離法提供了一種與集成測(cè)試不同的單元測(cè)試分離手段,它允許開發(fā)人員在軟件生命周期的單元測(cè)試階段專心致力于單元測(cè)試工作,而在軟件生命周期的集成測(cè)試階段專心致力于集成測(cè)試工作。只有分離法是純粹意義上適用于單元測(cè)試的方法,自上而下測(cè)試法和自下而上測(cè)試法適用于單元測(cè)試和集成階段的混合過程。與自上而下法和自下而上法不同的是,用分離法進(jìn)行的單元測(cè)試,被測(cè)單元不會(huì)受到與其關(guān)聯(lián)的其它任何單元的影響。
4.3 缺點(diǎn)
用分離法進(jìn)行單元測(cè)試最主要的缺點(diǎn)是它不能提供一個(gè)早期的單元集成。這必須要等到軟件生命周期的集成階段才能做到。(這很難說是一個(gè)真正的缺點(diǎn))
用分離法進(jìn)行單元測(cè)試時(shí)需要結(jié)構(gòu)設(shè)計(jì)信息和使用測(cè)試樁、測(cè)試驅(qū)動(dòng)。這會(huì)導(dǎo)致在測(cè)試靠近底層的單元時(shí),所花費(fèi)成本要高于自下而上法。然而,這個(gè)缺陷可以通過簡(jiǎn)化層次較高的單元的測(cè)試,以及每個(gè)單元每次發(fā)生變化時(shí)的較低花費(fèi)得到補(bǔ)償。
4.4 總結(jié)
用分離法進(jìn)行單元測(cè)試是最合適的選擇。在加上適當(dāng)?shù)募刹呗宰鳛檠a(bǔ)充,將會(huì)縮短軟件開發(fā)時(shí)間所占比例和降低開發(fā)費(fèi)用,這個(gè)優(yōu)勢(shì)將會(huì)貫穿整個(gè)軟件開發(fā)過程和軟件生命周期。按照分離法進(jìn)行單元測(cè)試時(shí),被測(cè)單元可以按照自上而下或者自下而上的順序進(jìn)行集成,或者集成為任何便利的群組和群組的結(jié)合。然而,一個(gè)自下而上的集成方式是與目前流行的面向?qū)ο蠛兔嫦驅(qū)ο蟮脑O(shè)計(jì)最相兼容的策略。分離法單元測(cè)試是實(shí)現(xiàn)高層次結(jié)構(gòu)覆蓋的最佳手段,而高層次結(jié)構(gòu)覆蓋對(duì)于很多標(biāo)準(zhǔn)所要求的高完善性和安全的關(guān)鍵性應(yīng)用來說是至關(guān)重要。在通過單元測(cè)試完成了所有實(shí)現(xiàn)好的結(jié)構(gòu)覆蓋的困難工作的基礎(chǔ)上,集成測(cè)試就可以集中于全面的功能測(cè)試和單元交互的測(cè)試。
5. 使用AdaTEST 和Cantata
一個(gè)單元的測(cè)試在整個(gè)軟件生命周期中要重復(fù)進(jìn)行很多次,無論是在開發(fā)階段還是維護(hù)過程中。一些測(cè)試工具如:AdaTEST 和Cantata,可以用于一些易于重復(fù)進(jìn)行和花費(fèi)較少的自動(dòng)化單元測(cè)試中,這樣可以有效降低人為因素帶來的風(fēng)險(xiǎn)。AdaTEST 和Cantata 測(cè)試腳本由一個(gè)測(cè)試驅(qū)動(dòng)和一個(gè)樁的集合(可選的)組成。AdaTEST 和Cantata 可以用于本文所介紹的任何單元測(cè)試的組織方法,或者這些方法的任意組合,使得開發(fā)人員可以采用最適合于項(xiàng)目應(yīng)用的測(cè)試策略。IPL 提供了兩篇相關(guān)論文,如下所示:
“Achieving Testability when using Ada Packaging and Data Hiding Methods”“Testing C++ Objects”,論文“Testing C++ Objects”同樣詳細(xì)討論了在用自下而上法進(jìn)行單元測(cè)試時(shí),分離的類和層次等級(jí)的約束是如何引發(fā)問題的。文章介紹了分離單元測(cè)試法是如何成為唯一實(shí)用的處理分離的類和層次等級(jí)約束的途徑
變那個(gè)發(fā)生變化的測(cè)試單元,而對(duì)其它測(cè)試單元沒有任何影響。由此可以看出分離組織法的成本要低于自下而上組織法和自上而下組織法,特別是當(dāng)發(fā)生變化時(shí)其效果更加明顯。
分離法提供了一種與集成測(cè)試不同的單元測(cè)試分離手段,它允許開發(fā)人員在軟件生命周期的單元測(cè)試階段專心致力于單元測(cè)試工作,而在軟件生命周期的集成測(cè)試階段專心致力于集成測(cè)試工作。只有分離法是純粹意義上適用于單元測(cè)試的方法,自上而下測(cè)試法和自下而上測(cè)試法適用于單元測(cè)試和集成階段的混合過程。與自上而下法和自下而上法不同的是,用分離法進(jìn)行的單元測(cè)試,被測(cè)單元不會(huì)受到與其關(guān)聯(lián)的其它任何單元的影響。
4.3 缺點(diǎn)
用分離法進(jìn)行單元測(cè)試最主要的缺點(diǎn)是它不能提供一個(gè)早期的單元集成。這必須要等到軟件生命周期的集成階段才能做到。(這很難說是一個(gè)真正的缺點(diǎn))
用分離法進(jìn)行單元測(cè)試時(shí)需要結(jié)構(gòu)設(shè)計(jì)信息和使用測(cè)試樁、測(cè)試驅(qū)動(dòng)。這會(huì)導(dǎo)致在測(cè)試靠近底層的單元時(shí),所花費(fèi)成本要高于自下而上法。然而,這個(gè)缺陷可以通過簡(jiǎn)化層次較高的單元的測(cè)試,以及每個(gè)單元每次發(fā)生變化時(shí)的較低花費(fèi)得到補(bǔ)償。
4.4 總結(jié)
用分離法進(jìn)行單元測(cè)試是最合適的選擇。在加上適當(dāng)?shù)募刹呗宰鳛檠a(bǔ)充,將會(huì)縮短軟件開發(fā)時(shí)間所占比例和降低開發(fā)費(fèi)用,這個(gè)優(yōu)勢(shì)將會(huì)貫穿整個(gè)軟件開發(fā)過程和軟件生命周期。按照分離法進(jìn)行單元測(cè)試時(shí),被測(cè)單元可以按照自上而下或者自下而上的順序進(jìn)行集成,或者集成為任何便利的群組和群組的結(jié)合。然而,一個(gè)自下而上的集成方式是與目前流行的面向?qū)ο蠛兔嫦驅(qū)ο蟮脑O(shè)計(jì)最相兼容的策略。分離法單元測(cè)試是實(shí)現(xiàn)高層次結(jié)構(gòu)覆蓋的最佳手段,而高層次結(jié)構(gòu)覆蓋對(duì)于很多標(biāo)準(zhǔn)所要求的高完善性和安全的關(guān)鍵性應(yīng)用來說是至關(guān)重要。在通過單元測(cè)試完成了所有實(shí)現(xiàn)好的結(jié)構(gòu)覆蓋的困難工作的基礎(chǔ)上,集成測(cè)試就可以集中于全面的功能測(cè)試和單元交互的測(cè)試。
5. 使用AdaTEST 和Cantata
一個(gè)單元的測(cè)試在整個(gè)軟件生命周期中要重復(fù)進(jìn)行很多次,無論是在開發(fā)階段還是維護(hù)過程中。一些測(cè)試工具如:AdaTEST 和Cantata,可以用于一些易于重復(fù)進(jìn)行和花費(fèi)較少的自動(dòng)化單元測(cè)試中,這樣可以有效降低人為因素帶來的風(fēng)險(xiǎn)。AdaTEST 和Cantata 測(cè)試腳本由一個(gè)測(cè)試驅(qū)動(dòng)和一個(gè)樁的集合(可選的)組成。AdaTEST 和Cantata 可以用于本文所介紹的任何單元測(cè)試的組織方法,或者這些方法的任意組合,使得開發(fā)人員可以采用最適合于項(xiàng)目應(yīng)用的測(cè)試策略。IPL 提供了兩篇相關(guān)論文,如下所示:
“Achieving Testability when using Ada Packaging and Data Hiding Methods”“Testing C++ Objects”,論文“Testing C++ Objects”同樣詳細(xì)討論了在用自下而上法進(jìn)行單元測(cè)試時(shí),分離的類和層次等級(jí)的約束是如何引發(fā)問題的。文章介紹了分離單元測(cè)試法是如何成為唯一實(shí)用的處理分離的類和層次等級(jí)約束的途徑
評(píng)論