一、覆蓋率定義
作為一個測試人員,保證產品的軟件質量是其工作首要目標,為了這個目標,測試人員常常會通過很多手段或工具來加以保證,覆蓋率就是其中一環比較重要的環節。
我們通常會將測試覆蓋率分為兩個部分,即“需求覆蓋率”和“代碼覆蓋率”。
需求覆蓋:指的是測試人員對需求的了解程度,根據需求的可測試性來拆分成各個子需求點,來編寫相應的測試用例,最終建立一個需求和用例的映射關系,以用例的測試結果來驗證需求的實現,可以理解為黑盒覆蓋。
代碼覆蓋:為了更加全面的覆蓋,我們可能還需要理解被測程序的邏輯,需要考慮到每個函數的輸入與輸出,邏輯分支代碼的執行情況,這個時候我們的測試執行情況就以代碼覆蓋率來衡量,可以理解為白盒覆蓋。
以上兩者完全可以相輔相成,用代碼覆蓋結果反向的檢查需求覆蓋(用例)的測試是否充分完整。
如果做覆蓋率測試?我們可以借助一些網上流行的各種覆蓋率工具,本章主要介紹JaCoCo這個工具。
二、JAVA覆蓋率工具介紹
市場上java主要代碼覆蓋率工具:EMMA、JaCoCo。
總結一下個人對JaCoCo優勢的理解:
(1) JaCoCo支持分支覆蓋、引入了Agent模式。
(2) EMMA官網已經不維護了,JaCoCo是其團隊開發的,可以理解為一個升級版。
(3) JaCoCo社區比較活躍,官網也在不斷的維護更新。
我們前期使用的EMMA,也做了全量、差異覆蓋率,和精准耦合也結合在了一起,但后來考慮到JaCoCo的優勢,也就全部切換了過來。
2.1 JaCoCo簡述
JaCoCo是一個開源的覆蓋率工具(官網地址:http://www.eclemma.org/JaCoCo/),它針對的開發語言是java,其使用方法很靈活,可以嵌入到Ant、Maven中;可以作為Eclipse插件,可以使用其JavaAgent技術監控Java程序等等。
很多第三方的工具提供了對JaCoCo的集成,如sonar、Jenkins等。
JaCoCo包含了多種尺度的覆蓋率計數器,包含指令級覆蓋(Instructions,C0coverage),分支(Branches,C1coverage)、圈復雜度(CyclomaticComplexity)、行覆蓋(Lines)、方法覆蓋(non-abstract methods)、類覆蓋(classes),后面會一一介紹。
我們先看看其覆蓋率結果展現如下圖1-1所示,方便讀者先有一個大概的了解。
圖1-1 覆蓋率報告結果部分截圖
標示綠色的為行覆蓋充分,標紅色的為未覆蓋的行,黃色菱形的為分支部分覆蓋,綠色菱形為分支完全覆蓋。
通過這個報告的結果就可以知道代碼真實的執行情況,便於我們分析評估結果。
2.2 JaCoCo基本概念
行覆蓋率:度量被測程序的每行代碼是否被執行,判斷標准行中是否至少有一個指令被執行。
類覆蓋率:度量計算class類文件是否被執行。
分支覆蓋率:度量if和switch語句的分支覆蓋情況,計算一個方法里面的總分支數,確定執行和不執行的 分支數量。
方法覆蓋率:度量被測程序的方法執行情況,是否執行取決於方法中是否有至少一個指令被執行。
指令覆蓋:計數單元是單個java二進制代碼指令,指令覆蓋率提供了代碼是否被執行的信息,度量完全 獨立源碼格式。
圈復雜度:在(線性)組合中,計算在一個方法里面所有可能路徑的最小數目,缺失的復雜度同樣表示測 試案例沒有完全覆蓋到這個模塊。
2.3 JaCoCo 原理
1、注入方式介紹
這個圖包含了幾種不同的收集覆蓋率信息的方法,每種方法的實現方法都不一樣,帶顏色的部分是JaCoCo比較有特色的地方。
上面各個名次含義(帶顏色的為JaCoCo支持):
上表JaCoCo支持的部分,再詳細的解釋下:
(1) JaCoCo在Byte Code時使用的ASM技術修改字節碼方法,可以修改Jar文件、class文件字節碼文件。
(2) JaCoCo同時支持on-the-fly和offline的兩種插樁模式。
On-the-fly插樁:
JVM中通過-javaagent參數指定特定的jar文件啟動Instrumentation的代理程序,代理程序在通過Class Loader裝載一個class前判斷是否轉換修改class文件,將統計代碼插入class,測試覆蓋率分析可以在JVM執行測試代碼的過程中完成。
Offline模式:
在測試前先對文件進行插樁,然后生成插過樁的class或jar包,測試插過樁 的class和jar包后,會生成動態覆蓋信息到文件,最后統一對覆蓋信息進行處理,並生成報告。
On-the-fly和offline比較:
On-the-fly模式更方便簡單進行代碼覆蓋分析,無需提前進行字節碼插樁,無需考慮classpath 的設置。
存在如下情況不適合on-the-fly,需要采用offline提前對字節碼插樁:
(1) 運行環境不支持java agent。
(2) 部署環境不允許設置JVM參數。
(3) 字節碼需要被轉換成其他的虛擬機如Android Dalvik VM。
(4) 動態修改字節碼過程中和其他agent沖突。
(5) 無法自定義用戶加載類。
2、JaCoCo執行最小的java版本
最小需要Java1.5
3、字節碼處理方式
JaCoCo通過注入來修改和生成java字節碼,使用的是ASM庫。
4、java方法控制流分析
JaCoCo是如何在字節碼注入的?
我們帶着疑問來看下面的內容:
先舉個實例,有個java方法:
編譯后轉換成字節碼后,內容如下:
我們知道JaCoCo是字節碼注入方式,它是通過一個Probe探針的方式來注入的,具體如下:
探針是字節指令集插入到java方法中,程序執行后可以被記錄,它不會改變原有代碼的行為。
我們看看探針前后插入比較:
顏色的部分就是探針注入的地方。
JaCoCo是根據控制流Type來采用不同的探針插入策略的。
一個用java字節碼定義的java方法的控制流圖可能有以下的type,每一個type連接一個源指令與目標指令,type不同探針的注入策略也會不同,如下是type定義:
探針不改變該方法的行為,但記錄他們已被執行的事實,從理論上講,可以在控制流圖的每一個邊插入一個探針,作為探針實現本身需要多個字節碼指令,這將增加幾倍的類文件的大小和執行速度。
事實上,只需要一個幾個探頭,根據每個方法的控制流的方法,下面說明了如何在不同的邊緣類型的情況下添加額外的指令:
一個instrumented class可以用以下代碼檢索其探針數組實例:
JaCoCo是用一個布爾數組來實現探針,每個探針對應於該數組中的項。當以下四個字節碼指令觸發時探針進行輸入設置為true:
JaCoCo對行探針是這樣處理的,添加兩行指令之間的一個額外的探針時,后續行至少包含一個方法調用。
以上是JaCoCo插樁原理,如果想深入了解,可以去看看它的源碼實現。
三、JaCoCo使用方式
使用方式有很多,這里貼出了相應的參考鏈接,根據項目的不同可以靈活供有需要的讀者去學習。
3.1 Apache Ant方式
參見 http://eclemma.org/jacoco/trunk/doc/ant.html
主要有以下幾種,具體使用就不介紹了,應用寶是用的這種方式,后續有介紹。
Task coverage、Task agent、Task dump、Task merge、Task report、Task instrument
3.2 命令行方式
參見 http://www.eclemma.org/jacoco/trunk/doc/agent.html
使用方式說明:
主要放在JAVA_OPTS中,比如:
由AgentOptions的getVMArgument方法加載,各參數入AgentOptions的對應參數,為后續操作做為輸入。
下面是官網的所有參數說明:
系統在jvm停止的時候會dump覆蓋率信息。
關鍵的核心代碼在這里,Agent.java在有一段代碼
Runtime.getRuntime().addShutdownHook這個方法的意思就是在jvm中增加一個關閉的鈎子,當jvm關閉的時候,會執行系統中已經設置的所有通過方法addShutdownHook添加的鈎子,當系統執行完這些鈎子后,jvm才會關閉。所以這些鈎子可以在jvm關閉的時候進行內存清理、對象銷毀等操作。
也就是在JVM關閉的時候調用agent.shutdown(),也就是寫覆蓋率數據。
3.3 Apache Maven方式
參見 http://www.eclemma.org/jacoco/trunk/doc/maven.html
這種方式適合Maven的項目。
下面簡單說下調用方式原理:
就拿官方的Offline Example來說吧,其部分內容如下:
注意藍色的部分,上面的配置主要做了以下幾個事情:
(1) 項目已jar包方式打包,引入junit和jacoco。
(2) Build時執行instrument、report、check。
(3) 覆蓋率生成到target/jacoco.exec
我們看看他是怎么觸發調用的。
在jacoco源碼中:jacoco-maven-plugin\target\classes\META-INF\maven\org.jacoco\jacoco-maven-plugin目錄下有個plugin-help.xml文件,它里面標明了具體的調用方式。
截出instrument這段,關鍵地方就是下面藍色部分。
官網上關於參數的說明:
給出一個整理后的表格:
再給一個jacoco的maven部分的代碼目錄:
到這里,大家應該清楚其調用的方式了吧。
3.4 Eclipse EclDmma Plugin方式
具體步驟如下:
(1) 在Eclipse菜單中選擇Help → Install New Software...
(2) 在安裝彈框中輸入http://update.eclemma.org/,勾選出現的版本。
(3) 核對版本,點擊Next。
(4) 根據向導完成安裝。
(5) 使用就不說了。
3.5 與Jekins集成
(1) 先要在jenkins上安裝JaCoCo的插件,安裝完成之后在job的配置項中可以增加這個選項(如圖1-2):
圖1-2
(2) 選擇后出現(圖1-3):
圖1-3
第一個錄入框是你的覆蓋率文件(exec),第二個是class文件目錄,第三個是源代碼文件目錄。
(3) 配置好了之后進行構建,構建完成之后job首頁就會出現覆蓋率的趨勢圖(圖1-4),鼠標點擊趨勢圖可以看到覆蓋率詳情(圖1-5) ,包括具體覆蓋率數據和源碼的覆蓋率情況:
圖1-4 趨勢圖
圖1-5 覆蓋率詳情
未完待續 :
JaCoCo原理篇就介紹到這里了,后續還有項目實踐篇和踩坑篇,實踐篇主要介紹下JaCoCo在實際業務中的使用情況,踩坑篇里面包含了幾個當時遇到的比較棘手的問題的解決思路,有興趣的童鞋請關注。
轉自:https://cloud.tencent.com/developer/article/1038055
看了阿里開發手冊 “單元測試第八條的推薦”
單元測試的基本目標:語句覆蓋率達到 70% ;核心模塊的語句覆蓋率和分支覆蓋率都
要達到 100%
Jacoco用法
首先在Maven中引入一下插件
1 <!-- 代碼測試覆蓋率 --> 2 <plugin> 3 <groupId>org.jacoco</groupId> 4 <artifactId>jacoco-maven-plugin</artifactId> 5 <version>0.7.8</version> 6 <executions> 7 <execution> 8 <id>prepare-agent</id> 9 <goals> 10 <goal>prepare-agent</goal> 11 </goals> 12 </execution> 13 <execution> 14 <id>report</id> 15 <phase>prepare-package</phase> 16 <goals> 17 <goal>report</goal> 18 </goals> 19 </execution> 20 </executions> 21 </plugin>
然后Maven執行 mvn clean install -Dmaven.test.failure.ignore=true
后面紅色划重點,意思是:如果在單元測試中,出現了錯誤,那么忽略他,繼續執行下去。
這樣的好處是,Maven可以執行完,然后生成代碼覆蓋率,否則一報錯,就不會生成代碼覆蓋率了。
Jacoco生成內容
我們可以在你的項目中 /target/site/jacoco/index.html 找到代碼覆蓋率
打開后如上圖,紅色代碼沒有覆蓋到的代碼,綠色代表已經覆蓋到的代碼。
Jacoco 則是使用字節碼注入(Byte Code Instrumentation)的方式,使用 ASM 庫在字節碼中插入 Probe 探針,通過統計運行時探針的覆蓋情況來統計覆蓋率信息。

On-the-fly 模式:
JVM 中通過 javaagent 參數指定特定的 jar 文件啟動 Instrumentation 的代理程序,代理程序在通過 Class Loader 裝載一個 class 前判斷是否轉換修改 class文件,將統計代碼插入 class,測試覆蓋率分析可以在 JVM 執行測試代碼的過程中完成。
Offline 模式:
在測試前先對文件進行插樁,然后生成插過樁的 class 或 jar 包,測試插過樁的 class 和 jar 包后,會生成動態覆蓋信息到文件,最后統一對覆蓋信息進行處理,並生成報告。
存在如下情況不適合 on-the-fly,需要采用 offline 提前對字節碼插樁:
- 運行環境不支持 java agent。
- 部署環境不允許設置 JVM 參數。
- 字節碼需要被轉換成其他的虛擬機如 Android Dalvik VM。
- 動態修改字節碼過程中和其他 agent 沖突。
- 無法自定義用戶加載類。
作者:LensAclrtn
鏈接:https://www.jianshu.com/p/c6fafb50b07a

既然 Jacoco 是依據 class 文件進行覆蓋率的統計,那么在用 EclEmma 合並會話數據時,應該保證多個會話的所測試 class 文件字節碼內容是相同的,即多次測試過程中被測試 Java 類的源文件沒有被修改並且重新編譯過。所以在 Eclipse 中,測試用例開始執行執行后,應該保證 Testee 源文件不被改動。如果修改了被測試源文件並保存( Eclipse 會自動重新編譯),請將之前的所有測試用例重新以 Coverage As 模式執行一般,否則合並后的覆蓋率測試數據會有誤差。
另外,由於 JaCoCo 分析統計的是編譯后的 class 文件中字節碼指令的執行情況。例如某源文件中有一個靜態的方法 someMethod,但是在編譯時 Javac 會自動為我們的類生成一個構造方法(本例中沒有提供非空的構造方法),所以這個類同時有 someMethod 和一個構造方法。由於在執行靜態方法過程中沒有調用到構造函數,所以會顯示覆蓋率不是100%
作者:LensAclrtn
鏈接:https://www.jianshu.com/p/c6fafb50b07a