基於Jacoco的單元測試程式碼覆蓋率統計

陳琦發表於2023-05-04

程式碼覆蓋率(Code coverage)用於描述一次測試執行中,被執行的程式碼佔總程式碼的比率。這裡的一次執行,可以是一次單元測試,也可以是一段時間內的手工測試執行;覆蓋率除了按程式碼行進行計算,也可以統計程式程式碼的分支、函式和類的覆蓋比率。

本文中介紹的Jacoco是一款流行的開源Java程式碼覆蓋率工具,其覆蓋率指標主要有以下幾個:

  • 指令 Instructions

針對Java位元組程式碼指令的覆蓋,是Jacoco最小粒度的指標。它提供總量、被執行和遺漏和數量資訊。

  • 分支 Branches

基於If、Switch語句分支來計算覆蓋率,不包括異常處理分支。可在檔案、類和方法3個層面進行統計。

  • 行、方法和類

針對原始碼的中的行、方法和類,進行總數、被執行、遺漏數量的統計。

下面我們給出一個例子,展示如何在Maven執行單元測試時,使用Jacoco統計程式碼覆蓋率。完整被測專案的原始碼可參考這裡

新建一個基於Maven的TestNG單元測試專案,在pom.xml中加入以下依賴。

<dependency>
  <groupId>org.testng</groupId>
  <artifactId>testng</artifactId>
  <version>7.0.0</version>
</dependency>

<dependency>
  <groupId>com.google.inject</groupId>
  <artifactId>guice</artifactId>
  <version>3.0</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.uncommons</groupId>
  <artifactId>reportng</artifactId>
  <version>1.1.4</version>
  <scope>test</scope>
  <exclusions>
    <exclusion>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
    </exclusion>
  </exclusions>
</dependency>

在pom.xml中增加以下2個外掛,分別用於TestNG測試執行和Jacoco覆蓋率統計。

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.22.1</version>
  <configuration>
    <testFailureIgnore>true</testFailureIgnore>
    <suiteXmlFiles>
      <suiteXmlFile>${testng.suite}</suiteXmlFile>
    </suiteXmlFiles>
    <argLine> @{argLine} -Dfile.encoding=UTF-8</argLine>
  </configuration>
</plugin>

<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.8.9</version>
  <executions>
    <execution>
      <goals>
        <goal>prepare-agent</goal>
      </goals>
    </execution>
    <execution>
      <id>report</id>
      <phase>test</phase>
      <goals>
        <goal>report</goal>
      </goals>
    </execution>
  </executions>
</plugin>

下面我們編寫一個被測試的Java類Count。

package com.deeptest.sample.jcoco;

public class Count {
    public int add(int x ,int y){
        int i = 0;
        if (i == 0) {
            i++;
        } else {
            i--;
        }

        return x + y;
    }

    public int sub(int x ,int y){
        return x - y;
    }

    public int div(int x ,int y){
        return x / y;
    }
}

再寫一個TestNG單元測試類。

package com.deeptest.sample.jcoco;

import org.testng.annotations.Test;
import static org.testng.AssertJUnit.assertEquals;

public class CountTest {
    @Test
    public void testAdd() {
        Count count = new Count();

        int result = count.add(2,2);
        assertEquals(result, 4);
    }
}

在src/test/res中建立一個測試套件,它被pom.xml中的maven-surefire-plugin外掛呼叫,並會在maven構建的test生命週期中執行。

<?xml version="1.0" encoding="UTF-8"?>
<suite name="套件">
    <test name="用例">
        <classes>
            <class name="com.deeptest.sample.jcoco.CountTest" />
        </classes>
    </test>
</suite>

進入專案根目錄,執行以下命令開始測試。第一次執行會下載相應的依賴,時間會相對比較久一些。

mvn clean test

結束後jacoco會在target/site/jacoco下生成XML和HTML格式的覆蓋率報告,其中HTML報告用瀏覽器開啟後如下。
image.png

最後,我們花點時間給大家介紹下maven構建的過程。在pom.xml我們定義了兩個外掛,分別用於jacoco的執行和java程式碼的打包。
這裡重點介紹下jacoco-maven-plugin外掛,其下名為prepare-agent的goal,預設會在maven的initialize生命週期中執行(pom.xml中不配置phase的goal,會預設使用外掛的設定),用於準備代理(你可以理解為向Java虛擬機器注入程式碼,新增獲取VM執行資訊的鉤子);名為report的goal,用於在測試完成後,生成覆蓋率報告。

如果不使用pom.xml的execution執行器,也可以用以下命令來代替。注意:它是一整行的命令,為了加註釋分成了多行。

mvn clean -Dmaven.test.skip=false                              # 不忽略測試
    org.jacoco:jacoco-maven-plugin:0.8.9:prepare-agent         # 準備Jacoco代理
    compile test-compile                                       # 編譯程式碼
    org.apache.maven.plugins:maven-surefire-plugin:2.22.1:test # 單元測試
    org.apache.maven.plugins:maven-jar-plugin:2.4:jar          # 打包JAR
    org.jacoco:jacoco-maven-plugin:0.8.9:report                # 生成Jacoco覆蓋率報告
    -Dmaven.test.failure.ignore=true -Dfile.encoding=UTF-8     # 一些引數

專題目錄

相關文章