測試覆蓋率

測試覆蓋率通常被用來衡量測試的充分性和完整性,從廣義的角度來講,測試覆蓋率主要分為兩大類,一類是面向項目的需求覆蓋率,另一類是更偏向技術(shù)的代碼覆蓋率。

需求覆蓋率

需求覆蓋率是指測試對需求的覆蓋程度,通常的做法是將每一條分解后的軟件需求和對應(yīng)的測試建立一對多的映射關(guān)系,最終目標是保證測試可以覆蓋每個需求,以保證軟件產(chǎn)品的質(zhì)量。

我們通常采用?ALM,Doors?和?TestLink?等需求管理工具來建立需求和測試的對應(yīng)關(guān)系,并以此計算測試覆蓋率。

需求覆蓋率統(tǒng)計方法屬于傳統(tǒng)瀑布模型下的軟件工程實踐,傳統(tǒng)瀑布模型追求自上而下地制定計劃、分析需求、設(shè)計軟件、編寫代碼、測試和運維等,在流程上是重量級的,已經(jīng)很難適應(yīng)當(dāng)今互聯(lián)網(wǎng)時代下的敏捷開發(fā)實踐。

所以,互聯(lián)網(wǎng)測試項目中很少直接基于需求來衡量測試覆蓋率,而是將軟件需求轉(zhuǎn)換成測試需求,然后基于測試需求再來設(shè)計測試點。

因此,現(xiàn)在人們口中的測試覆蓋率,通常默認指代碼覆蓋率,而不是需求覆蓋率。

代碼覆蓋率

簡單來說,代碼覆蓋率是指,至少被執(zhí)行了一次的條目數(shù)占整個條目數(shù)的百分比。

如果“條目數(shù)”是語句,對應(yīng)的就是代碼行覆蓋率;如果“條目數(shù)”是函數(shù),對應(yīng)的就是函數(shù)覆蓋率;如果“條目數(shù)”是路徑,那么對應(yīng)的就是路徑覆蓋率。依此類推,你就可以得到絕大多數(shù)常見的代碼覆蓋率類型的定義。

這里簡單介紹一下最常用的三種代碼覆蓋率指標。

(1)行覆蓋率又稱為語句覆蓋率,指已經(jīng)被執(zhí)行到的語句占總可執(zhí)行語句(不包含類似?C++?的頭文件聲明、代碼注釋、空行等等)的百分比。這是最常用也是要求最低的覆蓋率指標。實際項目中通常會結(jié)合判定覆蓋率或者條件覆蓋率一起使用。

(2)判定覆蓋又稱分支覆蓋,用以度量程序中每一個判定的分支是否都被測試到了,即代碼中每個判斷的取真分支和取假分支是否各被覆蓋至少各一次。比如,對于?if(a>0?&&?b>0),就要求覆蓋“a>0?&&?b>0”為?TURE?和?FALSE?各一次。

(3)條件覆蓋是指,判定中的每個條件的可能取值至少滿足一次,度量判定中的每個條件的結(jié)果?TRUE?和?FALSE?是否都被測試到了。比如,對于?if(a>0?&&?b>0),就要求“a>0”取?TRUE?和?FALSE?各一次,同時要求“b>0”取?TRUE?和?FALSE?各一次。

代碼覆蓋率的價值

現(xiàn)在很多項目都在單元測試以及集成測試階段統(tǒng)計代碼覆蓋率,但是我想說的是,統(tǒng)計代碼覆蓋率僅僅是手段,你必須透過現(xiàn)象看到事物的本質(zhì),才能從根本上保證軟件整體的質(zhì)量。

統(tǒng)計代碼覆蓋率的根本目的是找出潛在的遺漏測試用例,并有針對性的進行補充,同時還可以識別出代碼中那些由于需求變更等原因造成的不可達的廢棄代碼。

通常我們希望代碼覆蓋率越高越好,代碼覆蓋率越高越能說明你的測試用例設(shè)計是充分且完備的,但你也會發(fā)現(xiàn)測試的成本會隨著代碼覆蓋率的提高以類似指數(shù)級的方式迅速增加。

如果想達到?70%?的代碼覆蓋率,你可能只需要?30?分鐘的時間成本。但如果你想把代碼覆蓋率提高到?90%,那么為了這額外的?20%,你可能花的時間就遠不止?30?分鐘了。更進一步,你如果想達到?100%?的代碼覆蓋率,可想而知你花費的代價就會更大了。

那么,為什么代碼覆蓋率的提高,需要付出越來越大的代價呢?因為在后期,你需要大量的樁代碼、Mock?代碼和全局變量的配合來控制執(zhí)行路徑。

所以,在軟件企業(yè)中,只有單元測試階段對代碼覆蓋率有較高的要求。因為從技術(shù)實現(xiàn)上講,單元測試可以最大化地利用打樁技術(shù)來提高覆蓋率。而你如果想在集成測試或者是?GUI?測試階段將代碼覆蓋率提高到一定量級,那你所要付出的代價是巨大的,而且在很多情況下根本就實現(xiàn)不了。

代碼覆蓋率的局限性

我先來問你一個問題,如果你通過努力,已經(jīng)把某個函數(shù)的?MC/DC?代碼覆蓋率(MC/DC?覆蓋率是最高標準的代碼覆蓋率指標,除了直接關(guān)系人生命安全的軟件以外,很少會有項目會有嚴格的?MC/DC?覆蓋率要求)做到了?100%,軟件質(zhì)量是否就真的高枕無憂、萬無一失了呢?

很不幸,即使你所設(shè)計的測試用例已經(jīng)達到?100%?的代碼覆蓋率,軟件產(chǎn)品的質(zhì)量也做不到萬無一失。其根本原因在于代碼覆蓋率的計算是基于現(xiàn)有代碼的,并不能發(fā)現(xiàn)那些“未考慮某些輸入”以及“未處理某些情況”形成的缺陷。

我給你舉個極端的例子,如果一個被測函數(shù)里面只有一行代碼,只要這個函數(shù)被調(diào)用過了,那么衡量這一行代碼質(zhì)量的所有覆蓋率指標都會是?100%,但是這個函數(shù)是否真正實現(xiàn)了應(yīng)該需要實現(xiàn)的功能呢?

顯然,代碼覆蓋率反映的僅僅是已有代碼的哪些邏輯被執(zhí)行過了,哪些邏輯還沒有被執(zhí)行過。以此為依據(jù),你可以補充測試用例,可以去測試那些還沒有覆蓋到的執(zhí)行路徑。但也是僅此而已,對于那些壓根還沒有代碼實現(xiàn)的部分,基于代碼覆蓋率的統(tǒng)計指標就無能為力了。

總結(jié)來講,高的代碼覆蓋率不一定能保證軟件的質(zhì)量,但是低的代碼覆蓋率一定不能能保證軟件的質(zhì)量。

好了,現(xiàn)在你已經(jīng)了解了代碼覆蓋率的概念、價值和局限性,那么接下來,我就以?Java?代碼覆蓋率工具為例,給你解釋一下代碼覆蓋率工具的內(nèi)部實現(xiàn)原理以及一些關(guān)鍵技術(shù)。

當(dāng)你理解了這部分內(nèi)容,以后再面對各個不同開發(fā)語言的不同代碼覆蓋率工具時,就可以做到胸有成竹地根據(jù)具體的項目性質(zhì),選擇最合適的代碼覆蓋率工具了。

代碼覆蓋率工具

JaCoCo?是一款?Java?代碼的主流開源覆蓋率工具,可以很方便地嵌入到?Ant、Maven?中,并且和很多主流的持續(xù)集成工具以及代碼靜態(tài)檢查工具,比如?Jekins?和?Sonar?等,都有很好的集成。

首先,我先帶你看看?JaCoCo?的代碼覆蓋率報告長什么樣子。

如圖?1?所示為?JaCoCo?的整體代碼覆蓋率統(tǒng)計報告,包括了每個?Java?代碼文件的行覆蓋率以及分支覆蓋率統(tǒng)計,并給出了每個?Java?代碼文件的行數(shù)、方法數(shù)和類數(shù)等具體信息。

JaCoCo 代碼覆蓋率統(tǒng)計報告實例

如圖?2?所示為每個?Java?文件內(nèi)部詳細的代碼覆蓋率情況,圖中綠色的行表示已經(jīng)被覆蓋,紅色的行表示尚未被覆蓋,黃色的行表示部分覆蓋;左側(cè)綠色菱形塊表示該分支已經(jīng)被完全覆蓋、黃色菱形塊表示該分支僅被部分覆蓋。

JaCoCo詳細代碼覆蓋率實例

顯然,通過這個詳盡的報告,你就可以知道代碼真實的執(zhí)行情況、哪些代碼未被覆蓋。以此為基礎(chǔ),你再去設(shè)計測試用例就會更有針對性了。

代碼覆蓋率工具的實現(xiàn)原理

JaCoCo?的詳細報告,讓你驚嘆于代碼覆蓋率工具的強大。但你有沒有仔細想過,這樣的統(tǒng)計信息如何被獲取到的呢?

統(tǒng)計代碼覆蓋率的不同注入實現(xiàn)技術(shù)

實現(xiàn)代碼覆蓋率的統(tǒng)計,最基本的方法就是注入(Instrumentation)。簡單地說,注入就是在被測代碼中自動插入用于覆蓋率統(tǒng)計的探針(Probe)代碼,并保證插入的探針代碼不會給原代碼帶來任何影響。

對于?Java?代碼來講,根據(jù)注入目標的不同,可以分為源代碼(Source?Code)注入和字節(jié)碼(Byte?Code)注入兩大類。基于?JVM?本身特性以及執(zhí)行效率的原因,目前主流的工具基本都是使用字節(jié)碼注入,注入的具體實現(xiàn)采用?ASM?技術(shù)。

ASM?是一個?Java?字節(jié)碼操縱框架,能被用來動態(tài)生成類或者增強既有類的功能,可以直接產(chǎn)生?class?文件,也可以在類被加載入?JVM?之前動態(tài)改變類行為。

根據(jù)注入發(fā)生的時間點,字節(jié)碼注入又可以分為兩大模式:On-The-Fly?注入模式和?Offline?注入模式。

第一,On-The-Fly?注入模式

On-The-Fly?模式的特點在于無需修改源代碼,也無需提前進行字節(jié)碼插樁。它適用于支持?Java?Agent?的運行環(huán)境。

這樣做的優(yōu)點是,可以在系統(tǒng)不停機的情況下,實時收集代碼覆蓋率信息。缺點是運行環(huán)境必須允許使用?Java?Agent。

實現(xiàn)?On-The-Fly?模式,主要有兩種技術(shù)方案:

(1)開發(fā)自定義的類裝載器(Class?Loader)實現(xiàn)類裝載策略,每次類加載前,需要在?class?文件中插入探針,早期的?Emma?就是使用這種方案實現(xiàn)的探針插入;

(2)借助?Java?Agent,利用執(zhí)行在?main()?方法之前的攔截器方法?premain()?來插入探針,實際使用過程中需要在?JVM?的啟動參數(shù)中添加“-javaagent”并指定用于實時字節(jié)碼注入的代理程序,這樣代理程序在裝載每個?class?文件前,先判斷是否已經(jīng)插入了探針,如果沒有則需要將探針插入?class?文件中,目前主流的?JaCoCo?就是使用了這個方式。

第二,Offline?注入模式

Offline?模式也無需修改源代碼,但是需要在測試開始之前先對文件進行插樁,并事先生成插過樁的?class?文件。它適用于不支持?Java?Agent?的運行環(huán)境,以及無法使用自定義類裝載器的場景。

這樣做的優(yōu)點是,JVM?啟動時不再需要使用?Java?Agent?額外開啟代理,缺點是無法實時獲取代碼覆蓋率信息,只能在系統(tǒng)停機時下獲取。

Offline?模式根據(jù)是生成新的?class?文件還是直接修改原?class?文件,又可以分為?Replace?和?Inject?兩種不同模式。

和?On-The-Fly?注入模式不同,Replace?和?Inject?的實現(xiàn)是,在測試運行前就已經(jīng)通過?ASM?將探針插入了?class?文件,而在測試的運行過程中不需要任何額外的處理。Cobertura?就是使用?Offline?模式的典型代表。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容