https://blog.csdn.net/TMQ1225/article/details/52221187?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control
JAVA代碼覆蓋率工具JaCoCo-原理篇
一、 覆蓋率定義
作為一個(gè)測(cè)試人員,保證產(chǎn)品的軟件質(zhì)量是其工作首要目標(biāo),為了這個(gè)目標(biāo),測(cè)試人員常常會(huì)通過(guò)很多手段或工具來(lái)加以保證,覆蓋率就是其中一環(huán)比較重要的環(huán)節(jié)。
我們通常會(huì)將測(cè)試覆蓋率分為兩個(gè)部分,即“需求覆蓋率”和“代碼覆蓋率”。
需求覆蓋:指的是測(cè)試人員對(duì)需求的了解程度,根據(jù)需求的可測(cè)試性來(lái)拆分成各個(gè)子需求點(diǎn),來(lái)編寫(xiě)相應(yīng)的測(cè)試用例,最終建立一個(gè)需求和用例的映射關(guān)系,以用例的測(cè)試結(jié)果來(lái)驗(yàn)證需求的實(shí)現(xiàn),可以理解為黑盒覆蓋。
代碼覆蓋:為了更加全面的覆蓋,我們可能還需要理解被測(cè)程序的邏輯,需要考慮到每個(gè)函數(shù)的輸入與輸出,邏輯分支代碼的執(zhí)行情況,這個(gè)時(shí)候我們的測(cè)試執(zhí)行情況就以代碼覆蓋率來(lái)衡量,可以理解為白盒覆蓋。
以上兩者完全可以相輔相成,用代碼覆蓋結(jié)果反向的檢查需求覆蓋(用例)的測(cè)試是否充分完整。
如果做覆蓋率測(cè)試?我們可以借助一些網(wǎng)上流行的各種覆蓋率工具,本章主要介紹JaCoCo這個(gè)工具。
二、JAVA覆蓋率工具介紹
市場(chǎng)上java主要代碼覆蓋率工具:EMMA、JaCoCo
總結(jié)一下個(gè)人對(duì)JaCoCo優(yōu)勢(shì)的理解:
(1)JaCoCo支持分支覆蓋、引入了Agent模式。
(2)EMMA官網(wǎng)已經(jīng)不維護(hù)了,JaCoCo是其團(tuán)隊(duì)開(kāi)發(fā)的,可以理解為一個(gè)升級(jí)版。
(3)JaCoCo社區(qū)比較活躍,官網(wǎng)也在不斷的維護(hù)更新。
我們前期使用的EMMA,也做了全量、差異覆蓋率,和精準(zhǔn)耦合也結(jié)合在了一起,但后來(lái)考慮到JaCoCo的優(yōu)勢(shì),也就全部切換了過(guò)來(lái)。
2.1 JaCoCo簡(jiǎn)述
JaCoCo是一個(gè)開(kāi)源的覆蓋率工具(官網(wǎng)地址:http://www.eclemma.org/JaCoCo/),它針對(duì)的開(kāi)發(fā)語(yǔ)言是java,其使用方法很靈活,可以嵌入到Ant、Maven中;可以作為Eclipse插件,可以使用其JavaAgent技術(shù)監(jiān)控Java程序等等。
很多第三方的工具提供了對(duì)JaCoCo的集成,如sonar、Jenkins等。
JaCoCo包含了多種尺度的覆蓋率計(jì)數(shù)器,包含指令級(jí)覆蓋(Instructions,C0coverage),分支(Branches,C1coverage)、圈復(fù)雜度(CyclomaticComplexity)、行覆蓋(Lines)、方法覆蓋(non-abstract methods)、類(lèi)覆蓋(classes),后面會(huì)一一介紹。
我們先看看其覆蓋率結(jié)果展現(xiàn)如下圖1-1所示,方便讀者先有一個(gè)大概的了解。

標(biāo)示綠色的為行覆蓋充分,標(biāo)紅色的為未覆蓋的行,黃色菱形的為分支部分覆蓋,綠色菱形為分支完全覆蓋。
通過(guò)這個(gè)報(bào)告的結(jié)果就可以知道代碼真實(shí)的執(zhí)行情況,便于我們分析評(píng)估結(jié)果。
2.2 JaCoCo基本概念
行覆蓋率:度量被測(cè)程序的每行代碼是否被執(zhí)行,判斷標(biāo)準(zhǔn)行中是否至少有一個(gè)指令被執(zhí)行。
類(lèi)覆蓋率:度量計(jì)算class類(lèi)文件是否被執(zhí)行。
分支覆蓋率:度量if和switch語(yǔ)句的分支覆蓋情況,計(jì)算一個(gè)方法里面的
總分支數(shù),確定執(zhí)行和不執(zhí)行的 分支數(shù)量。
方法覆蓋率:度量被測(cè)程序的方法執(zhí)行情況,是否執(zhí)行取決于方法中是否有至少一個(gè)指令被執(zhí)行。
指令覆蓋:計(jì)數(shù)單元是單個(gè)java二進(jìn)制代碼指令,指令覆蓋率提供了代碼是否被執(zhí)行的信息,度量完全 獨(dú)立源碼格式。
圈復(fù)雜度:在(線(xiàn)性)組合中,計(jì)算在一個(gè)方法里面所有可能路徑的最小數(shù)目,缺失的復(fù)雜度同樣表示測(cè) 試案例沒(méi)有完全覆蓋到這個(gè)模塊。
2.3 JaCoCo 原理

1. 注入方式介紹
這個(gè)圖包含了幾種不同的收集覆蓋率信息的方法,每種方法的實(shí)現(xiàn)方法都不一樣,帶顏色的部分是JaCoCo比較有特色的地方。
上面各個(gè)名次含義(帶顏色的為JaCoCo支持):
上表JaCoCo支持的部分,再詳細(xì)的解釋下:
(1)JaCoCo在Byte Code時(shí)使用的ASM技術(shù)修改字節(jié)碼方法,可以修改Jar文件、class文件字節(jié)碼文件。
(2)JaCoCo同時(shí)支持on-the-fly和offline的兩種插樁模式。
On-the-fly插樁:
JVM中通過(guò)-javaagent參數(shù)指定特定的jar文件啟動(dòng)Instrumentation的代理程序,代理程序在通過(guò)Class Loader裝載一個(gè)class前判斷是否轉(zhuǎn)換修改class文件,將統(tǒng)計(jì)代碼插入class,測(cè)試覆蓋率分析可以在JVM執(zhí)行測(cè)試代碼的過(guò)程中完成。
Offline模式:
在測(cè)試前先對(duì)文件進(jìn)行插樁,然后生成插過(guò)樁的class或jar包,測(cè)試插過(guò)樁 的class和jar包后,會(huì)生成動(dòng)態(tài)覆蓋信息到文件,最后統(tǒng)一對(duì)覆蓋信息進(jìn)行處理,并生成報(bào)告。
On-the-fly和offline比較:
On-the-fly模式更方便簡(jiǎn)單進(jìn)行代碼覆蓋分析,無(wú)需提前進(jìn)行字節(jié)碼插樁,無(wú)需考慮classpath 的設(shè)置。
存在如下情況不適合on-the-fly,需要采用offline提前對(duì)字節(jié)碼插樁:
(1)運(yùn)行環(huán)境不支持java agent。
(2)部署環(huán)境不允許設(shè)置JVM參數(shù)。
(3)字節(jié)碼需要被轉(zhuǎn)換成其他的虛擬機(jī)如Android Dalvik VM。
(4)動(dòng)態(tài)修改字節(jié)碼過(guò)程中和其他agent沖突。
(5)無(wú)法自定義用戶(hù)加載類(lèi)。
2. JaCoCo執(zhí)行最小的java版本
最小需要Java1.5
3. 字節(jié)碼處理方式
JaCoCo通過(guò)注入來(lái)修改和生成java字節(jié)碼,使用的是ASM庫(kù)。
4. java方法控制流分析
JaCoCo是如何在字節(jié)碼注入的?
我們帶著疑問(wèn)來(lái)看下面的內(nèi)容:
先舉個(gè)實(shí)例,有個(gè)java方法:
編譯后轉(zhuǎn)換成字節(jié)碼后,內(nèi)容如下:
我們知道JaCoCo是字節(jié)碼注入方式,它是通過(guò)一個(gè)Probe探針的方式來(lái)注入的,具體如下:
探針是字節(jié)指令集插入到j(luò)ava方法中,程序執(zhí)行后可以被記錄,它不會(huì)改變?cè)写a的行為。
我們看看探針前后插入比較:
顏色的部分就是探針注入的地方。
JaCoCo是根據(jù)控制流Type來(lái)采用不同的探針插入策略的。
一個(gè)用java字節(jié)碼定義的java方法的控制流圖可能有以下的type,每一個(gè)type連接一個(gè)源指令與目標(biāo)指令,type不同探針的注入策略也會(huì)不同,如下是type定義:
探針不改變?cè)摲椒ǖ男袨?,但記錄他們已被?zhí)行的事實(shí),從理論上講,可以在控制流圖的每一個(gè)邊插入一個(gè)探針,作為探針實(shí)現(xiàn)本身需要多個(gè)字節(jié)碼指令,這將增加幾倍的類(lèi)文件的大小和執(zhí)行速度。
事實(shí)上,只需要一個(gè)幾個(gè)探頭,根據(jù)每個(gè)方法的控制流的方法,下面說(shuō)明了如何在不同的邊緣類(lèi)型的情況下添加額外的指令:
一個(gè)instrumented class可以用以下代碼檢索其探針數(shù)組實(shí)例:
JaCoCo是用一個(gè)布爾數(shù)組來(lái)實(shí)現(xiàn)探針,每個(gè)探針對(duì)應(yīng)于該數(shù)組中的項(xiàng)。當(dāng)以下四個(gè)字節(jié)碼指令觸發(fā)時(shí)探針進(jìn)行輸入設(shè)置為true:
JaCoCo對(duì)行探針是這樣處理的,添加兩行指令之間的一個(gè)額外的探針時(shí),后續(xù)行至少包含一個(gè)方法調(diào)用。
以上是JaCoCo插樁原理,如果想深入了解,可以去看看它的源碼實(shí)現(xiàn)。
三、JaCoCo使用方式
使用方式有很多,這里貼出了相應(yīng)的參考鏈接,根據(jù)項(xiàng)目的不同可以靈活供有需要的讀者去學(xué)習(xí)。
3.1 Apache Ant方式
參見(jiàn) http://eclemma.org/jacoco/trunk/doc/ant.html
主要有以下幾種,具體使用就不介紹了,應(yīng)用寶是用的這種方式,后續(xù)有介紹。
Task coverage、Task agent、Task dump、Task merge、Task report、Task instrument
3.2 命令行方式
參見(jiàn) http://www.eclemma.org/jacoco/trunk/doc/agent.html
使用方式說(shuō)明:
主要放在JAVA_OPTS中,比如:
由AgentOptions的getVMArgument方法加載,各參數(shù)入AgentOptions的對(duì)應(yīng)參數(shù),為后續(xù)操作做為輸入。
下面是官網(wǎng)的所有參數(shù)說(shuō)明:
系統(tǒng)在jvm停止的時(shí)候會(huì)dump覆蓋率信息。
關(guān)鍵的核心代碼在這里,Agent.java在有一段代碼
Runtime.getRuntime().addShutdownHook這個(gè)方法的意思就是在jvm中增加一個(gè)關(guān)閉的鉤子,當(dāng)jvm關(guān)閉的時(shí)候,會(huì)執(zhí)行系統(tǒng)中已經(jīng)設(shè)置的所有通過(guò)方法addShutdownHook添加的鉤子,當(dāng)系統(tǒng)執(zhí)行完這些鉤子后,jvm才會(huì)關(guān)閉。所以這些鉤子可以在jvm關(guān)閉的時(shí)候進(jìn)行內(nèi)存清理、對(duì)象銷(xiāo)毀等操作。
也就是在JVM關(guān)閉的時(shí)候調(diào)用agent.shutdown(),也就是寫(xiě)覆蓋率數(shù)據(jù)。
3.3 Apache Maven方式
參見(jiàn) http://www.eclemma.org/jacoco/trunk/doc/maven.html
這種方式適合Maven的項(xiàng)目。
下面簡(jiǎn)單說(shuō)下調(diào)用方式原理:
就拿官方的Offline Example來(lái)說(shuō)吧,其內(nèi)容如下:
注意藍(lán)色的部分,上面的配置主要做了以下幾個(gè)事情:
(1)項(xiàng)目已jar包方式打包,引入junit和jacoco。
(2)Build時(shí)執(zhí)行instrument、report、check。
(3)覆蓋率生成到target/jacoco.exec
我們看看他是怎么觸發(fā)調(diào)用的。
在jacoco源碼中:jacoco-maven-plugin\target\classes\META-INF\maven\org.jacoco\jacoco-maven-plugin目錄下有個(gè)plugin-help.xml文件,它里面標(biāo)明了具體的調(diào)用方式。
截出instrument這段,關(guān)鍵地方就是下面藍(lán)色部分。
官網(wǎng)上關(guān)于參數(shù)的說(shuō)明:
給出一個(gè)整理后的表格:
再給一個(gè)jacoco的maven部分的代碼目錄:
到這里,大家應(yīng)該清楚其調(diào)用的方式了吧。
3.4 Eclipse EclDmma Plugin方式
具體步驟如下:
(1)在Eclipse菜單中選擇Help → Install New Software…
(2)在安裝彈框中輸入http://update.eclemma.org/,勾選出現(xiàn)的版本。
(3)核對(duì)版本,點(diǎn)擊Next。
(4)根據(jù)向?qū)瓿砂惭b。
(5)使用就不說(shuō)了。
3.5 與Jekins集成
(1)先要在jenkins上安裝JaCoCo的插件,安裝完成之后在job的配置項(xiàng)中可以增加這個(gè)選項(xiàng)(如圖1-2):
圖1-2
(2)選擇后出現(xiàn)(圖1-3):
圖1-3
第一個(gè)錄入框是你的覆蓋率文件(exec),第二個(gè)是class文件目錄,第三個(gè)是源代碼文件目錄。
(3)配置好了之后進(jìn)行構(gòu)建,構(gòu)建完成之后job首頁(yè)就會(huì)出現(xiàn)覆蓋率的趨勢(shì)圖(圖1-4),鼠標(biāo)點(diǎn)擊趨勢(shì)圖可以看到覆蓋率詳情(圖1-5) ,包括具體覆蓋率數(shù)據(jù)和源碼的覆蓋率情況:
圖1-4 趨勢(shì)圖
圖1-5 覆蓋率詳情
JaCoCo原理篇就介紹到這里了,后續(xù)還有項(xiàng)目實(shí)踐篇和踩坑篇,實(shí)踐篇主要介紹下JaCoCo在實(shí)際業(yè)務(wù)中的使用情況,踩坑篇里面包含了幾個(gè)當(dāng)時(shí)遇到的比較棘手的問(wèn)題的解決思路,有興趣的童鞋請(qǐng)關(guān)注。
本章完~
原文鏈接:http://tmq.qq.com/2016/08/java-code-coverage-tools-jacoco-principle/
————————————————
版權(quán)聲明:本文為CSDN博主「騰訊移動(dòng)品質(zhì)中心TMQ」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/TMQ1225/article/details/52221187