Java程式碼覆蓋率工具Jacoco上手指引
JaCoCo是面向Java的開原始碼覆蓋率工具,JaCoCo以Java代理模式執行,它負責在執行測試時檢測位元組碼。 JaCoCo會深入研究每個指令,並顯示每個測試過程中要執行的行。 為了收集覆蓋率資料,JaCoCo使用ASM即時進行程式碼檢測,並在此過程中從JVM Tool Interface接收事件,最終生成程式碼覆蓋率報告。
jacoco執行有離線(offline)、線上(on the fly)模式之說,所謂線上模式就是在應用啟動時加入jacoco agent進行插樁,在開發、測試人員使用應用期間實時地進行程式碼覆蓋率分析。相信很多的java專案開發人員並不會去寫單元測試程式碼的,因此覆蓋率統計就要把手工測試或介面測試覆蓋的情況作為重要依據,顯然線上模式更符合實際需求,本文以線上模式為例進行演示。
Jacoco安裝
從官網
https://www.jacoco.org/jacoco/下載最新版本並解壓到指定目錄
主要用到如下二個jar包:
線上模式jacoco覆蓋率統計包含三個步驟:
- 啟動應用增加jacoco agent進行插樁,該步驟會啟動TCP Server。
- 從TCP Server dump生成程式碼覆蓋率檔案,生成的檔案格式為.exec格式的二級制檔案。
- 解析.exec格式檔案生成html格式程式碼覆蓋率報告。
準備Java示例程式碼
事先需要建立java示例程式碼,本文不聚焦java程式碼相關的內容,大家可以使用自己的java專案程式碼進行驗證,或者直接clone本文提供的spring boot示例程式碼倉庫
注意:線上模式使用的程式碼一定要是類似web應用這種永遠不會退出的程式為例,因為收集jacoco agent要啟用tcp server,如果程式執行完就退出了tpc server也關閉了。
示例程式碼就是提供兩個簡單的URL訪問,訪問截圖如下:
執行構建命令mvn clean package後,會在target目錄生成可執行的jar包
啟動jacoco agent進行插樁
開啟cmd,cd到示例程式碼的target目錄,執行如下命令:
java -javaagent:C:/Users/yuanw/Desktop/jacoco/lib/jacocoagent.jar=includes=*,output=tcpserver,port=6300,address=localhost,append=true -jar demo-0.0.1-SNAPSHOT.jar
關鍵引數說明:
- -javaagent:C:\工具\jacoco-0.8.6\lib\jacocoagent.jar=includes=*,這個引數就是啟用jacoco代理引數,其中C:\工具\jacoco-0.8.6\lib\jacocoagent.jar就是之前下載jacoco解壓後的jacocoagent.jar的絕對路徑,includes表示對要插樁的包進行過濾,*代表所有的class都要進行插樁,也可以根據情況進行過濾,如includes=com.mycompany.*
- output=tcpserver,這裡不需要改動,表示以TCP Server方式啟動應用並插樁
- port=6300,Jacoco開啟的TCP Server的埠,不能被佔用
- address=localhost,對外開放的地址,也可以指定IP地址
- demo-0.0.1-SNAPSHOT.jar,就是示例程式碼構建後target目錄生成的jar包,需要根據實際情況更新
執行後就會啟動web服務,截圖如下:
特別提醒:為了對比實時統計程式碼覆蓋率的效果,此時請先不要請求上面提到的任何一個URL,切記!dump生成覆蓋率檔案
保持服務啟動,再開啟一個cmd視窗同樣cd到target目錄,執行如下命令從上一步開啟的TCP Server中dump出覆蓋率檔案
java -jar C:\工具\jacoco-0.8.6\lib\jacococli.jar dump --address localhost --port 6300 --destfile ./jacoco_tcp_01.exec引數說明:
- --destfile ./jacoco_tcp.exec,其中./jacoco_tcp.exec為生成exec檔名,表示在當前目錄生成
- 其他引數和上一步類似,不再特別說明,注意需要更新jacococli.jar的絕對路徑
執行成功後,會在target目錄生成相應的.exec檔案
接下來,我們在瀏覽器中訪問該web示例應用提供的兩個
UR---http://127.0.0.1:8080/hello、http://127.0.0.1:8080/byebye,應用會呼叫相應的方法並正常返回結果。然後,我們再次執行dump命令再生成一個.exec檔案,命名為jacoco_tcp_02.exec
java -jar C:\工具\jacoco-0.8.6\lib\jacococli.jar dump --address localhost --port 6300 --destfile ./jacoco_tcp_02.exec分析exec檔案生成html報告
dump命令生成的.exec檔案為二進位制檔案,需要進行解析,以生成html報告為例,執行如下的命令分別將兩個.exec檔案解析成html報告
java -jar C:\工具\jacoco-0.8.6\lib\jacococli.jar report ./jacoco_tcp_01.exec --classfiles .\classes --sourcefiles ..\src\main\java --html report01
java -jar C:\工具\jacoco-0.8.6\lib\jacococli.jar report ./jacoco_tcp_01.exec --classfiles .\classes --sourcefiles ..\src\main\java --html report02引數說明:
- ./jacoco_tcp_01.exec,表示要解析的exec檔案的相對路徑
- --classfiles .\classes,需要指定生成的classes檔案目錄,參見下圖
- --sourcefiles ..\src\main\java,需要指定原始碼的檔案目錄,參見下圖
最終,target目錄會生成report01、report02兩個目錄,分別開啟其中的index.html檢視程式碼覆蓋率結果。
我們發現report01中的程式碼覆蓋率為73%,其中say()和bybye()兩個方法沒有被覆蓋:
而我們訪問了對應的服務之後生成的report02覆蓋率報告為100%,say()和bybye()這兩個方法都覆蓋到了:
覆蓋率指標說明
- Instructions: Java 位元組指令的覆蓋率。執行的最小單位,和程式碼的格式無關。
- Branches: 分支覆蓋率。注意,異常處理不算做分支。
- Cxty(Cyclomatic Complexity): 圈複雜度, Jacoco 會為每一個非抽象方法計算圈複雜度,併為類,包以及組(groups)計算複雜度。圈複雜度簡單地說就是為了覆蓋所有路徑,所需要執行單元測試數量,圈複雜度大說明程式程式碼可能質量低且難於測試和維護。
- Lines: 行覆蓋率,只要本行有一條指令被執行,則本行則被標記為被執行。
- Methods: 方法覆蓋率,任何非抽象的方法,只要有一條指令被執行,則該方法就會被計為被執行。
- Classes: 類覆蓋率,所有類,包括介面,只要其中有一個方法被執行,則標記為被執行。注意:建構函式和靜態初始化塊也算作方法。