介紹
如果你沒有執(zhí)行單元測試,你就會同時失去第一時間改善軟件質(zhì)量和削減開發(fā)時間和成本的機(jī)會。這種犧牲當(dāng)然是令人失望的。在此之前,開發(fā)人員一直沒有一種可行的方法來執(zhí)行單元測試。現(xiàn)在,JAVA開發(fā)人員能夠使用ParaSoft的Jtest自動化JAVA單元測試,而C/C++開發(fā)人員能夠利用ParaSoft最新的C++Test工具自動化C/C++單元測試。
本文闡述了執(zhí)行單元測試的方法、優(yōu)點和難點,然后描述了C++Test任何能夠給C/C++開發(fā)人員帶來巨大的好處。
什么是單元測試?
我們對單元測試的定義是測試應(yīng)用中最小的單元,如C/C++中的一個類。C/C++單元測試的目的是執(zhí)行每個類中的每一個方法或函數(shù),檢測所有存在的功能性問題、錯誤和構(gòu)造弱點。發(fā)現(xiàn)這些典型的問題通常涉及到三種測試方法:黑盒測試、白盒測試和回歸測試。黑盒測試通過確定類的公共界面是否依照定義執(zhí)行,檢查類的功能性;執(zhí)行這種類型的測試不需要有關(guān)實現(xiàn)細(xì)節(jié)的知識。白盒測試通過確定類在遇到非預(yù)期輸入時執(zhí)行是否正確,檢查所有的類的方法和函數(shù)(包括保護(hù)和私有成員)的健壯性;執(zhí)行這種類型的測試需要對類的實現(xiàn)細(xì)節(jié)的完整知識。回歸測試檢查是否類的修改會在原來正確的代碼中引入新的錯誤。
什么是單元測試的優(yōu)點?
單元測試被公認(rèn)為軟件開發(fā)過程中的一個關(guān)鍵步驟。單元測試能夠簡化錯誤檢測,在減少開發(fā)時間和成本的同時提高軟件質(zhì)量。
單元測試促進(jìn)錯誤檢測的第一個方面是使你更接近錯誤。圖1和圖2說明了這一點。
圖1:應(yīng)用測試
圖1顯示了一個包含許多對象的應(yīng)用的測試模型。大橢圓表示應(yīng)用,小橢圓表示對象,箭頭表示用戶輸入,紅星表示潛在的錯誤。
為了發(fā)現(xiàn)錯誤,必須修改輸入,對象間的相互作用將迫使某對象引發(fā)潛在的錯誤。這一點無疑是有一定難度的。想象一下你站在一張臺球桌前,所有的球圍成一個三角形放置在桌子中間,要求你一桿擊球?qū)⒅虚g的球打入指定的袋中。這是一件多么困難的事情啊!就像要設(shè)計一個輸入從而發(fā)現(xiàn)應(yīng)用中的一個錯誤一樣。由于其難度,開發(fā)人員只能依賴應(yīng)用軟件的運行失敗來發(fā)現(xiàn)錯誤,但卻仍然沒有測試到許多類。因此只做集成測試是一件非常困難的事情。
單元級測試提供了一種更有效的發(fā)現(xiàn)錯誤的方法。如圖2所示。
圖2:單元測試
單獨測試一個類時(與其它對象分離),由于更接近錯誤,找到潛在的錯誤就會變得容易得多。用上述臺球的例子來比喻,就像是臺面上只有壹兩個球時,一桿將一個球擊入指定的袋中。
單元測試促進(jìn)錯誤檢測的第二個方面是,將你從在問題中艱難跋涉去修正一個簡單錯誤的困境中解救出來。因為錯誤是相互關(guān)聯(lián)和作用的,在較高的層次上找到和修正一個錯誤,常常會發(fā)現(xiàn)另外的問題。當(dāng)你在較高層次上測試時,原始的錯誤就象是洋蔥的最內(nèi)層一樣,另外的錯誤就象緊包著內(nèi)層的其它層:只有把外層都剝掉才能看到最內(nèi)層。當(dāng)你測試每一個類時,錯誤還很少有機(jī)會建立在其它錯誤之上,并相互作用引起奇怪的行為。因此在單元級上檢測錯誤會容易的多。
最重要的結(jié)果是,更容易的錯誤檢測能夠在改善應(yīng)用質(zhì)量的同時大量削減開發(fā)時間和成本。首先,由于能夠更容易地找到錯誤,就會減少發(fā)現(xiàn)它們的時間和資源。其次,由于你一寫完一個類,就能發(fā)現(xiàn)和改正其中的錯誤,你就不需要在以后花費時間重新了解和摸索。最后,最重要的理由是:由于類的相互作用和關(guān)聯(lián)性,在單元級修改一個類只會影響到原始的類,而在較高的層次上修改一個類可能會改變多個程序部件的設(shè)計和功能性。越遲發(fā)現(xiàn)問題,通常就要修改越多的代碼。當(dāng)修改的代碼量增加時,兩個其它因素也會隨之增加:
-
修改每一個錯誤所需的時間和費用。
-
在代碼中引入新的錯誤的機(jī)會。
一次又一次的研究證明,隨著問題被檢測出來的時間的推遲,發(fā)現(xiàn)軟件錯誤所需的時間和成本會驚人地增加。請看下面的研究結(jié)果:
-
IBM: 根據(jù)IBM的一份內(nèi)部資料指出,確定軟件錯誤的相對成本是:在設(shè)計階段,1.5;編碼前,1;編碼中,1.5;測試前,10;測試中,60;交付后,100。[Watts Humphrey]
-
TRW: 確定錯誤的相對時間:需求分析階段,1;設(shè)計階段,3-6;編碼階段,10;開發(fā)測試階段,15-40;接受性測試階段,30-70;應(yīng)用運行中,40-1000。[Boehm]
-
IBM: 確定錯誤的相對時間:設(shè)計評審,1;代碼檢查,20;測試,82。[Remus]
-
JPL: Bush得出的每個錯誤的平均成本:編碼,US$90-US$120;測試,US$10,000。[Bush]
-
Freedman and Weinberg: 使用設(shè)計評審和代碼檢查手段的項目在測試時發(fā)現(xiàn)錯誤的數(shù)量會減少10倍,測試成本降低50%-8%,包括評審和檢查的成本。[Freedman]
什么是單元測試的難點?
基于上述信息,單元測試看上去就象一劑萬能藥。如果是這樣的話,為什么每一個C/C++開發(fā)人員不馬上對每一個類進(jìn)行單元測試?就目前可以使用的技術(shù)來說,對C/C++的單元測試是一件困難、煩瑣和耗時的事情,沒有很好的工具來自動化這一過程,使得許多C/C++開發(fā)人員望而生畏。
執(zhí)行單元測試的第一步是是目標(biāo)類變得可測。這需要兩個工作:
-
設(shè)計一個運行目標(biāo)類的測試驅(qū)動程序。
-
設(shè)計樁函數(shù),它們?yōu)楸粶y類所引用的任何外部資源返回值。
建立一個測試驅(qū)動,需要建立一個新的類,除了測試原始類以外它不能用于任何其它目的。測試驅(qū)動應(yīng)該具有下列特性:
-
一個指定設(shè)置和清除的標(biāo)準(zhǔn)方式。
-
一個選擇個別測試和所有有效測試的方法。
-
一個分析輸出的預(yù)期(或非預(yù)期)結(jié)果的機(jī)制。
-
一個標(biāo)準(zhǔn)的錯誤報告形式。
為了充分而正確地測試類,你需要設(shè)計一個能夠完全檢查被測類的測試驅(qū)動;若干次修改和重寫這樣一個測試驅(qū)動是免不了的。一旦建立了測試驅(qū)動,你必須仔細(xì)檢查它不能包含任何錯誤。測試驅(qū)動中的一個錯誤會破壞這個測試,但是你無法單獨測試一個類,你也不能測試測試驅(qū)動本身。
如果你的類引用任何還沒有準(zhǔn)備好或不可訪問的外部資源(如外部文件、數(shù)據(jù)庫和CORBA對象等),你必須建立相應(yīng)的樁函數(shù),它們的返回值類似于這些實際的外部資源應(yīng)該返回的值。當(dāng)建立這些樁函數(shù)時,你需要選擇樁函數(shù)的返回值,它們將影響程序的執(zhí)行路徑...
-
為了測試類的功能性必須執(zhí)行任何的路徑,
-
足夠的路徑能夠提供徹底的測試覆蓋性。
下一步是設(shè)計和建立合適的測試用例。為了徹底地測試類的結(jié)構(gòu)和功能性,你應(yīng)該設(shè)計兩中類型的測試用例:黑盒和白盒。
黑盒測試用例基于說明和規(guī)格文檔。特別地,至少應(yīng)該為規(guī)格文檔的每個入口建立一個測試用例;更好的是這些測試用例能夠測試每個入口的各種邊界條件。還需要為發(fā)現(xiàn)的每一個錯誤增加另外的測試用例,以及任何你認(rèn)為必要的其它測試。
白盒測試用例通過各種不同的輸入充分地執(zhí)行類的所有方法以發(fā)現(xiàn)缺陷。對于手工測試來說這是非常困難的。為了建立有效的白盒測試用例,你必須研究類的內(nèi)部結(jié)構(gòu),然后編寫測試用例盡可能完全地覆蓋類的所有方法,以及覆蓋所有可能引起類崩潰的輸入。要達(dá)到較高的測試覆蓋性,需要有效的白盒測試用例它們能夠執(zhí)行相當(dāng)多數(shù)的路徑。例如,一個典型的1萬行的程序,大約有1億條可能的路徑;手工建立能夠執(zhí)行所有這些路徑的輸入幾乎是不可行的。
在建立這些測試用例以后,你將要執(zhí)行整個測試用例并分析結(jié)果,確定在那里出現(xiàn)了錯誤、崩潰和薄弱環(huán)節(jié)。你還需測量這些測試的覆蓋性,以確定類被測試的程度以及需要追加的測試用例。
任何時候一個類被修改后,你應(yīng)該執(zhí)行回歸測試,保證沒有引入新的錯誤和/或原來的錯誤已經(jīng)被更正了。回歸測試包括白盒和黑盒測試用例,并且分析結(jié)果以確定類的質(zhì)量是否得到的改善。
C++Test: 一個自動化的單元測試解決方案
ParaSoft認(rèn)識到了C/C++單元測試內(nèi)在的的價值和難度,開發(fā)了C++Test,一個自動的C/C++單元測試工具,增強(qiáng)了ParaSoft的自動錯誤防止和錯誤檢測產(chǎn)品線。C++Test自動執(zhí)行所有可能的單元測試過程。特別地,它能夠自動:
-
建立每個被測類的測試驅(qū)動程序。
-
建立任何必要的樁函數(shù),并允許你定制這些樁函數(shù)的返回值或加入自己的樁函數(shù)。
-
單鍵執(zhí)行白盒測試的所有步驟。
-
生成黑盒測試用例的基礎(chǔ)集合。
-
運行黑盒測試用例。
-
生成黑盒測試的輸出結(jié)果。
-
執(zhí)行回歸測試。
-
跟蹤測試覆蓋性。
C++Test能夠測試所有類型的C/C++項目;C++Test甚至能夠支持COM對象,允許你對調(diào)用COM對象方法的類和方法執(zhí)行自動的單元測試。
C++Test還是高度可定制的;例如,你可以改變測試用例的生成參數(shù),過濾一定的文件、類或方法,在任何層次上(從這個項目到單個方法或測試用例)進(jìn)行測試。
另外,C++Test很容易與你目前的開發(fā)過程合作。C++Test直接安裝在DevStudio環(huán)境中,因此你能夠立即測試任何正在開發(fā)中的類。只要在Developer Studio的工具欄上按下Test File或Test Project按鈕,那么C++Test就會自動在C++Test圖形用戶界面中打開你的文件,為每個類建立一個測試驅(qū)動程序并自動測試每個類。
圖3:C++Test圖形用戶界面
通過自動化單元測試過程,C++Test使得單元測試切實可行,即使對時間非常緊張的項目和開發(fā)人員也是如此,并且比手工單元測試具有更好的精確行和正確行。這一切將轉(zhuǎn)化成更高質(zhì)量的應(yīng)用,并且以更短的時間以及更低的開發(fā)、支持和維護(hù)成本。
C++Test做什么?
1. 自動建立測試驅(qū)動和樁函數(shù)
C++Test自動建立一個測試驅(qū)動程序,其設(shè)計目標(biāo)是極大化類的測試覆蓋性和錯誤檢測。為類建立測試驅(qū)動,你只要簡單地打開這個類,然后按Build Test鍵。C++Test將自動建立測試驅(qū)動程序。
另外,如果被測的方法需要調(diào)用當(dāng)時還不存在或無法訪問的函數(shù),C++Test能夠自動生成樁函數(shù);這樣能夠測試與外部資源操作的交互作用和不包含任何隱藏的弱點。C++Test不是實際調(diào)用這些函數(shù),而是調(diào)用樁函數(shù)并返回樁函數(shù)提供的值。如果你需要控制使用的返回值,你可以建立一個樁調(diào)用表,生命輸入/輸出的關(guān)系。
圖4:建立樁調(diào)用表
你還能加入用戶定義的樁函數(shù)。例如,如果你要使用原始的函數(shù),且該函數(shù)定義在不同的文件中;或者你想要仿真原始函數(shù)的行為,而用一個簡單的函數(shù)替代它。
圖5:鍵入用戶自定義的樁調(diào)用
自動生成C/C++類的測試驅(qū)動程序和樁函數(shù)的能力是C++Test所獨有的;只有C++Test能夠自動測試C/C++類(一當(dāng)它能夠編譯時),而不需要用戶的任何干預(yù)。使得你能夠盡快地自動檢測代碼錯誤,以最容易、最省錢和最快速的方法找到和修正它們。如果沒有這樣的自動化工具,大量的時間和資源消耗將失去單元測試的潛在好處和現(xiàn)實意義。
2. 白盒測試
C++Test提供了一種有效并且高效的方法執(zhí)行白盒測試。C++Test完全自動執(zhí)行所有的白盒測試過程,自動生成和執(zhí)行精心設(shè)計的測試用例。自動標(biāo)記任何運行失敗,并以一種簡單的圖示化結(jié)構(gòu)顯示。然后自動保存這些測試用例,能夠方便地用于以后的回歸測試。
由于C++Test能夠自動生成樁函數(shù),或允許你加入自己的樁函數(shù),因此它能夠測試引用外部對象的類。換句話說,C++Test能夠運行任何一個或一組類,并自動生成和執(zhí)行一組測試用例,它們被設(shè)計成能夠發(fā)現(xiàn)盡可能多的錯誤。
圖6:執(zhí)行自動白盒測試
C++Test允許你定制白盒測試用例的生成,和在什么層次上(項目、文件、類或方法)執(zhí)行測試。
3. 黑盒測試
C++Test通過自動化黑盒測試的大部分操作,減輕了這類測試的負(fù)擔(dān)。特別是以兩種方法自動化黑盒測試的第一階段--建立測試用例:
你可以簡單地輸入測試用例輸入,然后讓C++Test運行測試用例并自動確定實際的輸出結(jié)果。如果結(jié)果正確,不需要其它動作。如果結(jié)果不正確,你可以輸入預(yù)期的輸出結(jié)果。這樣比手工輸入每個測試用例的結(jié)果更快更容易。
C++Test自動設(shè)計了一組廣譜的白盒測試用例。當(dāng)使用這些測試用例在黑盒測試時,你只需簡單地觀察實際的輸出結(jié)果,然后對任何不正確的結(jié)果輸入預(yù)期的值。
當(dāng)你需要輸入或修改測試用例時,你可以在C++Test自動生成的測試用例框架種簡單地鍵入相應(yīng)的值。這將顯著地加快建立測試用例的過程。
圖7:鍵入一個測試用例
在自動化建立黑盒測試用例的大多數(shù)步驟之外,C++Test完全自動化余下的黑盒測試步驟。按一個鍵,你能夠?qū)椖俊⑽募㈩惢蚍椒ㄟ\行一個或一組。C++Test然后自動執(zhí)行所有的測試用例,報告所有的輸入/輸出關(guān)系,并標(biāo)記任何實際輸出與預(yù)期不一致或?qū)е鲁绦虮罎⒌臏y試用例。
4. 回歸測試
C++Test完全自動化與回歸測試有關(guān)的所有步驟。C++Test首次測試某個類時,自動保存其測試和測試參數(shù)。當(dāng)需要執(zhí)行回歸測試時,你可以打開合適的項目和文件,運行所有原來的白盒和黑盒測試用例;C++Test會自動運行完全相同的測試用例和測試參數(shù),并告之發(fā)現(xiàn)的任何問題。這意味著你能夠立即知道修改是否引入了任何錯誤。
5. 測試覆蓋性分析
為了幫助你測量當(dāng)前使用的測試用例集合的有效性,并且給你提供達(dá)到盡可能高的覆蓋性的信息,C++Test自動監(jiān)視測試覆蓋性:
-
行覆蓋
-
累計行覆蓋
-
基本塊覆蓋
-
分支(判斷)覆蓋
-
條件覆蓋
-
MC/DC覆蓋(DO-178B標(biāo)準(zhǔn))
圖8:瀏覽測試結(jié)果
C++Test實時跟蹤測試覆蓋性,然后建立一個綜合測試覆蓋性報告。覆蓋性窗口圖示化地說明了當(dāng)前正在被執(zhí)行的代碼行,已執(zhí)行過的行和每行的執(zhí)行次數(shù)。因此,它不僅指出了一個代碼行是否被測試過,而且說明了被測試的有多徹底。這些信息對于確定那些代碼需要追加測試是非常有幫助的。
6. 集成的單步調(diào)試
如果你選擇在方法測試時捆綁調(diào)試器,C++Test將自動自動激活Microsoft Visual C++調(diào)試器,這樣使得你在用C++Test測試任何方法時仍然能夠方便地進(jìn)行單步調(diào)式。
7. 防止錯誤
C++Test能夠自動執(zhí)行兩種類型的變成標(biāo)準(zhǔn)。其內(nèi)建的特性允許你自動執(zhí)行動態(tài)的變成標(biāo)準(zhǔn),如“總是對每個類執(zhí)行單元測試”和“總是單步調(diào)試類”等。另外,假如你使用CodeWizard--ParaSoft的自動化可定制編程標(biāo)準(zhǔn)強(qiáng)化工具,C++Test可以自動運行CodeWizard。
8. 運行時錯誤檢測
C++Test還能幫助你執(zhí)行類一級的運行時錯誤自動檢測。如果你安裝了Insure++,C++Test可以自動運行類和方法通過Insure++,它將檢查下列錯誤:
-
內(nèi)存引用錯誤/內(nèi)存未初始化
-
內(nèi)存泄漏
-
內(nèi)存分配錯誤
-
變量定義沖突
-
I/O錯誤
-
指針錯誤
-
庫調(diào)用錯誤
-
邏輯錯誤
-
算法錯誤
這意味著你能夠在類開發(fā)的第一時間檢測運行時錯誤,而且無需自己做任何運行數(shù)據(jù)。C++Test自動生成大量而精心設(shè)計的測試用例,能夠幫助你更徹底、更方便和更快速地檢測運行時錯誤。
結(jié)論
通過執(zhí)行單元測試,你能夠有效地防止許多錯誤的出現(xiàn),盡早檢測出已存在的錯誤,并且比其它測試手段和技術(shù)更有效。影響C/C++開發(fā)人員執(zhí)行單元測試的主要障礙是需要消耗大量的時間的資源,目前的一些單元測試工具仍然存在著這樣的問題。C++Test的推出克服了這一障礙。C++Test做到了開發(fā)人員總是希望卻不敢相信的事情:自動化C/C++單元測試。
....