JaCoCo

JaCoCo(Java Code Coverage)是 Java 主流的代码覆盖率工具,通过字节码插桩统计测试执行了哪些代码,生成行覆盖、分支覆盖等报告,并可在 CI 中强制最低覆盖率阈值。

覆盖率指标

指标含义
行覆盖(Line)被执行的源码行占总行数的比例
分支覆盖(Branch)if/switch 分支被覆盖的比例(最能反映逻辑完整性)
指令覆盖(Instruction)JVM 字节码指令粒度,最细
方法覆盖(Method)被调用方法占总方法数
类覆盖(Class)被实例化/调用的类
圈复杂度(Complexity)独立执行路径数,等于最少测试用例数

Maven 集成

<!-- pom.xml -->
<build>
  <plugins>
    <plugin>
      <groupId>org.jacoco</groupId>
      <artifactId>jacoco-maven-plugin</artifactId>
      <version>0.8.11</version>
      <executions>
        <!-- 绑定 prepare-agent:在测试前注入 JaCoCo Agent -->
        <execution>
          <id>prepare-agent</id>
          <goals><goal>prepare-agent</goal></goals>
        </execution>
        <!-- 绑定 report:测试后生成 HTML/XML 报告 -->
        <execution>
          <id>report</id>
          <phase>verify</phase>
          <goals><goal>report</goal></goals>
        </execution>
        <!-- 绑定 check:低于阈值则构建失败 -->
        <execution>
          <id>check</id>
          <phase>verify</phase>
          <goals><goal>check</goal></goals>
          <configuration>
            <rules>
              <rule>
                <element>BUNDLE</element>      <!-- 整个项目维度 -->
                <limits>
                  <limit>
                    <counter>LINE</counter>
                    <value>COVEREDRATIO</value>
                    <minimum>0.80</minimum>    <!-- 最低 80% 行覆盖 -->
                  </limit>
                  <limit>
                    <counter>BRANCH</counter>
                    <value>COVEREDRATIO</value>
                    <minimum>0.70</minimum>    <!-- 最低 70% 分支覆盖 -->
                  </limit>
                </limits>
              </rule>
            </rules>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

常用命令:

mvn test                    # 运行测试,生成 target/jacoco.exec 数据文件
mvn verify                  # 运行测试 + 生成报告 + 覆盖率检查
mvn jacoco:report           # 单独生成报告(target/site/jacoco/index.html)
mvn jacoco:check            # 单独检查阈值

Gradle 集成

// build.gradle.kts
plugins {
    jacoco
}
 
jacoco {
    toolVersion = "0.8.11"
}
 
tasks.test {
    finalizedBy(tasks.jacocoTestReport)   // 测试完自动生成报告
}
 
tasks.jacocoTestReport {
    dependsOn(tasks.test)
    reports {
        xml.required = true               // CI 用 XML(SonarQube、Codecov 读取)
        html.required = true              // 本地查看 HTML
        csv.required = false
    }
}
 
tasks.jacocoTestCoverageVerification {
    violationRules {
        rule {
            limit {
                counter = "LINE"
                value = "COVEREDRATIO"
                minimum = "0.80".toBigDecimal()
            }
        }
        rule {
            limit {
                counter = "BRANCH"
                value = "COVEREDRATIO"
                minimum = "0.70".toBigDecimal()
            }
        }
    }
}
 
// check 任务依赖覆盖率验证(执行 ./gradlew check 时自动触发)
tasks.check {
    dependsOn(tasks.jacocoTestCoverageVerification)
}

排除不需要统计的类

配置文件、实体类、枚举、自动生成的代码通常不计入覆盖率:

<!-- Maven:在 report 和 check 的 configuration 中添加 -->
<configuration>
  <excludes>
    <!-- Lombok 生成的代码 -->
    <exclude>**/*$*.class</exclude>
    <!-- 配置类 -->
    <exclude>com/example/config/**</exclude>
    <!-- 实体 / VO -->
    <exclude>com/example/entity/**</exclude>
    <exclude>com/example/vo/**</exclude>
    <!-- MyBatis Mapper 接口 -->
    <exclude>com/example/mapper/**</exclude>
    <!-- 启动类 -->
    <exclude>com/example/Application.class</exclude>
    <!-- 枚举 -->
    <exclude>com/example/enums/**</exclude>
  </excludes>
</configuration>
// Gradle
tasks.jacocoTestReport {
    classDirectories.setFrom(
        files(classDirectories.files.map {
            fileTree(it) {
                exclude(
                    "**/config/**",
                    "**/entity/**",
                    "**/vo/**",
                    "**/mapper/**",
                    "**/enums/**",
                    "**/*Application.class"
                )
            }
        })
    )
}

查看 HTML 报告

target/site/jacoco/index.html     # Maven
build/reports/jacoco/test/html/index.html  # Gradle

报告颜色含义:

颜色含义
绿色行/分支已覆盖
黄色分支部分覆盖(如 if 只测了 true,没测 false)
红色完全未覆盖

多模块项目聚合报告

多模块项目各子模块有独立报告,需在父模块聚合:

<!-- 父 pom.xml:新增聚合 report 模块或在父模块执行聚合 -->
<execution>
  <id>report-aggregate</id>
  <phase>verify</phase>
  <goals><goal>report-aggregate</goal></goals>
  <configuration>
    <outputDirectory>${project.reporting.outputDirectory}/jacoco-aggregate</outputDirectory>
  </configuration>
</execution>

CI 集成(GitHub Actions)

# .github/workflows/test.yml
- name: Run tests with coverage
  run: mvn verify
 
- name: Upload coverage to Codecov
  uses: codecov/codecov-action@v3
  with:
    files: target/site/jacoco/jacoco.xml
    fail_ci_if_error: true
 
# 或上传到 SonarQube
- name: SonarQube Scan
  run: mvn sonar:sonar
       -Dsonar.projectKey=my-project
       -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml

与 Spring Boot 测试联动

Spring Boot 测试中 JaCoCo 无需额外配置,只要 mvn test / gradle test 执行了测试,JaCoCo Agent 会自动收集所有 @SpringBootTest@WebMvcTest@DataJpaTest 等的覆盖率数据并合并到同一份报告。

测试执行
  ├── @SpringBootTest(集成测试)
  ├── @WebMvcTest(Controller 层)
  ├── @DataJpaTest(Repository 层)
  └── 无 Context 单元测试
           │
    JaCoCo Agent 收集 → jacoco.exec
           │
    jacoco:report → HTML / XML 报告
           │
    jacoco:check  → 低于阈值则 BUILD FAILURE

相关链接

  • 测试 — Spring Boot 测试分层(JUnit 5、Mockito、Testcontainers)
  • Maven — JaCoCo Maven 插件生命周期绑定
  • Gradle — JaCoCo Gradle 插件配置
  • CI集成 — GitHub Actions / Jenkins 中的覆盖率门禁
  • 参数校验 — 校验逻辑的分支覆盖测试策略