JAVA 程式碼覆蓋率工具 JaCoCo-原理篇
一、 覆蓋率定義
作為一個測試人員,保證產品的軟體質量是其工作首要目標,為了這個目標,測試人員常常會通過很多手段或工具來加以保證,覆蓋率就是其中一環比較重要的環節。
我們通常會將測試覆蓋率分為兩個部分,即“需求覆蓋率”和“程式碼覆蓋率”。
需求覆蓋:指的是測試人員對需求的瞭解程度,根據需求的可測試性來拆分成各個子需求點,來編寫相應的測試用例,最終建立一個需求和用例的對映關係,以用例的測試結果來驗證需求的實現,可以理解為黑盒覆蓋。
程式碼覆蓋:為了更加全面的覆蓋,我們可能還需要理解被測程式的邏輯,需要考慮到每個函式的輸入與輸出,邏輯分支程式碼的執行情況,這個時候我們的測試執行情況就以程式碼覆蓋率來衡量,可以理解為白盒覆蓋。
以上兩者完全可以相輔相成,用程式碼覆蓋結果反向的檢查需求覆蓋(用例)的測試是否充分完整。
如果做覆蓋率測試?我們可以藉助一些網上流行的各種覆蓋率工具,本章主要介紹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方式
主要有以下幾種,具體使用就不介紹了,應用寶是用的這種方式,後續有介紹。
Task coverage、Task agent、Task dump、Task merge、Task report、Task instrument
3.2 命令列方式
使用方式說明:
主要放在JAVA_OPTS中,比如:
由AgentOptions的getVMArgument方法載入,各引數入AgentOptions的對應引數,為後續操作做為輸入。
下面是官網的所有引數說明:
系統在jvm停止的時候會dump覆蓋率資訊。
關鍵的核心程式碼在這裡,Agent.java在有一段程式碼
Runtime.getRuntime().addShutdownHook這個方法的意思就是在jvm中增加一個關閉的鉤子,當jvm關閉的時候,會執行系統中已經設定的所有通過方法addShutdownHook新增的鉤子,當系統執行完這些鉤子後,jvm才會關閉。所以這些鉤子可以在jvm關閉的時候進行記憶體清理、物件銷燬等操作。
也就是在JVM關閉的時候呼叫agent.shutdown(),也就是寫覆蓋率資料。
這種方式適合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...
(3)核對版本,點選Next。
(4)根據嚮導完成安裝。
(5)使用就不說了。
###3.5 與Jekins整合
(1)先要在jenkins上安裝JaCoCo的外掛,安裝完成之後在job的配置項中可以增加這個選項(如圖1-2):
(2)選擇後出現(圖1-3):
圖1-3第一個錄入框是你的覆蓋率檔案(exec),第二個是class檔案目錄,第三個是原始碼檔案目錄。
(3)配置好了之後進行構建,構建完成之後job首頁就會出現覆蓋率的趨勢圖(圖1-4),滑鼠點選趨勢圖可以看到覆蓋率詳情(圖1-5) ,包括具體覆蓋率資料和原始碼的覆蓋率情況:
圖1-4 趨勢圖 圖1-5 覆蓋率詳情JaCoCo原理篇就介紹到這裡了,後續還有專案實踐篇和踩坑篇,實踐篇主要介紹下JaCoCo在實際業務中的使用情況,踩坑篇裡面包含了幾個當時遇到的比較棘手的問題的解決思路,有興趣的童鞋請關注。
本章完~