Jacoco 統計的是全量代碼覆蓋率。它不僅支持生成單元測試的覆蓋率,也支持監控生成接口測試,功能測試的覆蓋率。
一. 工作原理
1.1. Jacoco 工作原理
先來看一下 Jacoco 工作原理,如下圖所示:
1.2. 工作步驟
- 對Java字節碼進行插樁,有on-the-fly和offline兩種方式。
- 執行測試用例,收集程序執行軌跡信息,支持通過dump將操作記錄從服務端傳輸到本地。
- 數據處理器結合程序執行軌跡信息和代碼結構信息分析生成代碼覆蓋率報告。
- 結合源碼和編譯后的文件,可以將代碼覆蓋率報告圖形化展示出來,如html,xml等文件格式。
二. Jacoco使用
2.1. Ant 項目
原文:
JaCoCo comes with Ant tasks to launch Java programs with execution recording and for creating coverage reports from the recorded data. Execution data can be collected and managed with the tasks coverage, agent, dump and merge. Reports in different formats are created with the report task. For offline instrumentation the task instrument can be used to prepare class files.
翻譯:
JaCoCo 附帶了Ant任務。它通過啟動帶有記錄執行功能的Java程序,來實現生成覆蓋率報告。其中任務(coverage, agent, dump and merge)用於收集和管理執行信息;任務 report 用於創建不同格式的報告。任務instrument 用於為 offline 模式 instrumentation 准備類文件。詳細說明見
注意:build.xml中,有特定的 compile 階段。請務必保證,有 debug="true" 這個配置,否則 jacoco 是無法注入的。有的時候ant項目生成的覆蓋率數據大小為0,就可以去排查下這里。
實例如下:
- on-the-fly Example: https://www.jacoco.org/jacoco/trunk/doc/examples/build/build.xml
- Offline Example: https://www.jacoco.org/jacoco/trunk/doc/examples/build/build-offline.xml
2.2. Maven 項目
JaCoCo Maven 插件定義了了如下goals
1. help
2. prepare-agent
3. prepare-agent-integration
4. merge
5. report
6. report-integration
7. report-aggregate
8. check
9. dump
10. instrument
11. restore-instrumented-classes
注意:Export MAVEN_OPTS參數時,后續的所有mvn命令,都會帶上此參數,因此相當於每次執行mvn命令,都會嘗試啟動代理,因此可能會出現address bind already in use之類的異常拋出。因此,我們只有在mvn tomcat7:run啟動服務器時才需要啟動代理,其他如mvn的編譯、install命令都不需要,所以在啟動之后,把MAVEN_OPTS參數置空,或者重啟一個terminal來執行命令
具體實例如下:
- on-the-fly (JUnit tests) Example: https://www.jacoco.org/jacoco/trunk/doc/examples/build/pom.xml
- on-the-fly (unit tests and integration test) Example: https://www.jacoco.org/jacoco/trunk/doc/examples/build/pom-it.xml
- Offline Example: https://www.jacoco.org/jacoco/trunk/doc/examples/build/pom-offline.xml
在執行mvn test時,即可得到對應的覆蓋率報告。
2.3. Java Agent
Java Agent 只支持 on-the-fly 模式插樁,即所有類文件在加載到JVM期間獨立於應用程序框架進行內存預處理。jacocoagent.jar 是JaCoCo發行版的一部分,可以使用以下JVM選項激活Java代理。詳細說明見
-javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2]
JaCoCo代理支持的選項如下:
選項 | 描述 | 默認 |
---|---|---|
destfile | 執行數據的輸出文件的路徑。 | jacoco.exec |
append | 如果設置為true並且執行數據文件已經存在,則將覆蓋數據附加到現有文件。如果設置為 false,則將替換現有的執行數據文件。 | true |
includes | 執行分析中應包含的類名列表。列表條目以冒號(:)分隔,可以使用通配符(*和?)。除了性能優化或技術角落案例,通常不需要此選項。 | * (所有類) |
excludes | 應從執行分析中排除的類名稱列表。列表條目以冒號(:)分隔,可以使用通配符(*和?)。除了性能優化或技術角落案例,通常不需要此選項。 | 空(不排除類) |
exclclassloader | 應從執行分析中排除的類加載器名稱的列表。列表條目以冒號(:)分隔,可以使用通配符(*和 ?)。如果特殊框架與JaCoCo代碼工具發生沖突,特別是無法訪問Java運行時類的類加載器,則可能需要此選項。 | sun.reflect.DelegatingClassLoader |
inclbootstrapclasses | 指定是否還應該檢測引導類加載器的類。謹慎使用此功能,需要大量包括/不包括調整。 | false |
inclnolocationclasses | 指定是否還應該檢測沒有源位置的類。通常這樣的類是在運行時產生的,例如通過模擬框架,因此在默認情況下被排除。 | false |
sessionid | 與執行數據一起寫入的會話標識符。沒有這個參數,代理就會創建一個隨機的標識符。 | 自動生成 |
dumponexit | 如果設置為true覆蓋數據,將在VM關閉時寫入。如果file指定了轉儲,或者輸出為tcpserver/ tcpclient 並且在虛擬機終止時連接處於打開狀態,則只能寫入轉儲。 | true |
output | 用於寫入覆蓋率數據的輸出方法。有效的選項是: file:在虛擬機終止執行數據寫入destfile屬性中指定的文件。 tcpserver:代理偵聽由address和port屬性指定的TCP端口上的傳入連接。執行數據被寫入到這個TCP連接。 tcpclient:啟動時,代理將連接到由address和port屬性指定的TCP端口。執行數據被寫入到這個TCP連接。 none:不要產生任何輸出。 請參閱下面的安全考慮。 |
file |
address | 當輸出方法為tcpserver或連接到 輸出方法時要綁定的IP地址或主機名 tcpclient。在tcpserver模式中,值“ *”使代理接受任何本地地址上的連接。 | 回環接口 |
port | 當輸出方法是綁定的端口,tcpserver或者當輸出方法是連接的端口tcpclient。在 tcpserver模式下,端口必須可用,這意味着如果多個JaCoCo代理應該在同一台機器上運行,則必須指定不同的端口。 | 6300 |
classdumpdir | agent所調用到的所有class文件的目錄。這可以用於調試目的,或者在動態創建類的情況下,例如當使用腳本引擎時。 | 沒有轉儲 |
jmx | 如果設置為true代理通過名稱下的JMX 公開 功能org.jacoco:type=Runtime。請參閱下面的安全考慮。 | false |
2.4. 命令行界面
JaCoCo帶有一個命令行接口,可以通過命令行執行基本操作 (jacococli.jar)。對於復雜的用途,尤其是大型項目,請使用 JaCoCo 與各種構建工具的集成。
具體命令如下。每個命令都有可選和必選參數列表。某些參數可以多次指定以提供多個值。
- dump ------Request execution data from a JaCoCo agent running in 'tcpserver' output mode.
- instrument------Off-line instrumentation of Java class files and JAR files.
- merge ------Merges multiple exec files into a new one.
- report ------Generate reports in different formats by reading exec and Java class files.
- classinfo ------Print information about Java class files at the provided location.
- execinfo ------Print exec file content in human readable format.
- version
三. 動態監測 Web 項目全量覆蓋率
來看下具體配置
3.1. 服務端設置 JacocoAgent 並啟動
- springBoot 項目為例
java -javaagent:/data/webapp/jacoco/jacocoagent.jar=includes=*,output=tcpserver,append=false,address=*,port=6200 -jar xxxxxxxxxx.jar
請注意 java -jar 命令的使用方式:在jar包前面傳進去的參數是給 jvm 啟動用的,在jar包之后跟的參數是給main方法的。
- tomcat 項目為例
我們常用的命令存在於:$CATALINA_HOME\bin下,有startup.sh和shutdown.sh,,其實這兩個只是封裝之后的腳本,底層調用的都是$CATALINA_HOME\bin\catalina.sh
set JAVA_OPTS=-server -Xms1024m -Xmx1024m -XX:PermSize=512M -XX:MaxNewSize=512m -XX:MaxPermSize=512m -Djava.awt.headless=true -javaagent:D:\AutoTest\jacoco\lib\jacocoagent.jar=includes=com.hundsun.*,output=tcpserver,port=8229,address=127.0.0.1 -Xverify:none
- weblogic 項目為例
在~/bin/startWebLogic.sh下配置jacoco環境信息
JAVA_OPTIONS="${JAVA_OPTIONS} -javaagent:/home/weblogic/Oracle/Middleware/Oracle_Home/user_projects/domains/base_domain/bin/jacoco-0.8.3/lib/jacocoagent.jar=includes=*,output=tcpserver,address=192.168.10.26,port=6200 -Xverify:none"
3.2. 執行測試(接口測試、功能測試等)
這時可以對服務端進行測試,所有調服務端接口的操作都會記錄作為代碼覆蓋率依據。
3.3. 抓取信息並生成報告
這步可以用jacococli.jar實現,也可以借助ant 或者maven。
3.3.1. 以 jacococli.jar為例
- 獲取exec文件
java -jar jacococli.jar dump --address 192.169.110.1 --port 2014 --destfile ./jacoco-demo.exec
使用這個命令會在我們調用方的服務上生成一個exec文件
- 生成report
java -jar jacococli.jar report ./jacoco-demo.exec --classfiles /Users/oukotoshuu/IdeaProjects/demo/target/classes/com --sourcefiles /Users/oukotoshuu/IdeaProjects/demo/src/main/java --html report --xml report.xml
注意這個classfiles和sourcefiles 可以是多個,我們如果是多模塊項目通過指定代碼路徑和編譯文件路徑去分開做統計
3.3.2. 以 ant 為例
- 在項目根目錄下新建一個build.xml文件,文件內容如下:
<?xml version="1.0" ?>
<project name="wftestReport" xmlns:jacoco="antlib:org.jacoco.ant" default="jacoco">
<property name="server_ip" value="10.16.55.95"/>
<property name="server_port" value="6200"/>
<!--Jacoco的安裝路徑-->
<property name="jacocoantPath" value="/data/jenkins/jacoco-0.8.4/lib/jacocoant.jar"/>
<!--最終生成.exec文件的路徑,Jacoco就是根據這個文件生成最終的報告的-->
<property name="jacocoexecPath" value="./jacoco/mergetest.exec"/>
<!--合並報告路徑-->
<property name="mergePath" value="./jacoco/all" />
<!--生成覆蓋率報告的路徑-->
<property name="reportfolderPath" value="./jacoco/report"/>
<!--跑的是class,標注的是源碼?-->
<!--源代碼路徑-->
<!--可以配置多個源代碼-->
<property name="express_src" value="/data/jenkins/.jenkins/workspace/TEST_ISTP_95/svn/src/main/java/com/isoftstone"/>
<!--.class文件路徑-->
<property name="express_class" value="/data/jenkins/.jenkins/workspace/TEST_ISTP_95/svn/target/classes/com/isoftstone"/>
<taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
<classpath path="${jacocoantPath}" />
</taskdef>
<!-- 1 dump任務:獲取覆蓋率exec文件 -->
<target name="dump">
<jacoco:dump address="${server_ip}" reset="false" destfile="${jacocoexecPath}" port="${server_port}" append="true"/>
</target>
<!-- 2 合並exec文件 -->
<!-- 獲取指定目錄下的所有 exec 文件並將數據合並為一個exec -->
<target name="merge">
<jacoco:merge destfile="./jacoco/merged.exec">
<fileset dir="${mergePath}" includes="*.exec" />
</jacoco:merge>
</target>
<!-- 3 生成覆蓋率報告
根據前面配置的源代碼路徑和.class文件路徑,
根據dump后,生成的.exec文件,生成最終的html覆蓋率報告。-->
<target name="report">
<!--暫時不刪除,一旦刪除,其他兩個的報告也沒了-->
<delete dir="${reportfolderPath}" />
<mkdir dir="${reportfolderPath}" />
<jacoco:report>
<executiondata>
<file file="${jacocoexecPath}" />
</executiondata>
<structure name="JaCoCo Report">
<group name="Core">
<classfiles>
<fileset dir="${express_class}" />
</classfiles>
<sourcefiles encoding="utf-8">
<fileset dir="${express_src}" />
</sourcefiles>
</group>
</structure>
<html destdir="${reportfolderPath}" encoding="utf-8" />
<csv destfile="D:\AutoTest\JRES\codeCoverage\report.csv" />
</jacoco:report>
</target>
</project>
-
接着在build.xml文件目錄下運行ant dump命令,在當前目錄下出現一個.exec后綴名結尾的文件,出現如下圖所示的代表運行成功
-
在build.xml文件目錄下運行ant report命令,生成report