






















一、是什么
JaCoCo 是 Java 生态最主流的代码覆盖率工具,能统计单元测试对代码的覆盖程度(哪些代码被执行、哪些没被执行),生成可视化报告,帮你发现测试遗漏的分支 / 代码行。
覆盖维度
二、核心原理
JaCoCo 基于字节码插桩(Instrumentation) 实现,无需修改源代码:
1、什么是字节码插桩?
2、常见使用场景:测试 / 监控工具
3、插桩分类
根据插桩的执行时机,分为 静态插桩 和 动态插桩,二者各有适用场景:
静态插桩(编译后 / 打包前修改字节码)
动态插桩(运行时修改字节码)
核心工具对比(JaCoCo 用 ASM,Mockito 用 ByteBuddy)
三、怎么用?
1、在 pom.xml 中配置 JaCoCo 插件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Test2</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<!-- 统一指定项目编码为 UTF-8 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 统一指定项目报告编码(可选,避免报告乱码) -->
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!-- JUnit 5 核心依赖 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<!-- 参数化测试依赖 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<!-- Mockito 核心依赖 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.11.0</version>
<scope>test</scope>
</dependency>
<!-- Mockito 适配 JUnit 5 的扩展 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.11.0</version>
<scope>test</scope>
</dependency>
<!-- 可选:静态方法Mock依赖(如需Mock静态工具类) -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.11.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!-- 执行快速、独立的单元测试(失败会直接中断构建)-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
</plugin>
<!-- 专门用于执行集成测试(Integration Tests) -->
<!--<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.1.2</version>
</plugin>-->
<!-- 1. JaCoCo 核心插件(生成覆盖率报告) -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version> <!-- 最新稳定版 -->
<configuration>
<excludes>
<!-- 排除配置类 -->
<exclude>com/multisig/config/**/*</exclude>
<!-- 排除工具类常量 -->
<exclude>com/multisig/constants/**/*</exclude>
<!-- 排除测试专用代码 -->
<exclude>**/*Test*.class</exclude>
</excludes>
</configuration>
<executions>
<!-- 执行测试时收集覆盖率数据 -->
<execution>
<id>jacoco-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<!-- 测试完成后生成覆盖率报告 -->
<execution>
<id>jacoco-report</id>
<phase>test</phase> <!-- 绑定到 mvn test 阶段 -->
<goals>
<goal>report</goal>
</goals>
</execution>
<!-- 可选:强制执行覆盖率规则(如行覆盖率≥80%) -->
<execution>
<id>jacoco-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<!-- 对所有代码的行覆盖率要求 ≥80% -->
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.8</minimum> <!-- 80% 行覆盖 -->
</limit>
<!-- 分支覆盖率 ≥70% -->
<limit>
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>0.7</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<!-- 2. 确保 Maven Surefire 插件适配 JUnit 5 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<includes>
<include>**/*Test.java</include> <!-- 执行所有测试类 -->
</includes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、被测代码
点击查看代码package com.example;
public class MultisigService {
private RpcClient rpcClient;
private int threshold = 2; // 多签门限
public MultisigService(RpcClient rpcClient) {
this.rpcClient = rpcClient;
}
// 核心方法:执行多签交易(含多个分支/异常)
public boolean executeMultisigTx(String txId, int signatureCount) {
// 分支1:空值校验
if (txId == null || txId.isEmpty()) {
return false;
}
// 分支2:签名数校验
if (signatureCount < threshold) {
return false;
}
// 分支3:RPC调用(含异常)
try {
return rpcClient.sendTx(txId);
} catch (RuntimeException e) {
// 异常分支
return false;
}
}
// 辅助方法:获取门限(未被测试覆盖)
public int getThreshold() {
return this.threshold;
}
}
package com.example;
public interface RpcClient {
boolean sendTx(String txId);
}
3、单元测试代码(JUnit 5 + Mockito)
点击查看代码package com.example;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class MultisigServiceTest {
@Mock
private RpcClient rpcClient;
@InjectMocks
private MultisigService multisigService;
@Test
@DisplayName("测试txId为空时返回false")
void testEmptyTxId() {
assertFalse(multisigService.executeMultisigTx("", 2));
}
@Test
@DisplayName("测试签名数不足时返回false")
void testInsufficientSignature() {
assertFalse(multisigService.executeMultisigTx("tx-001", 1));
}
@Test
@DisplayName("测试RPC返回成功时返回true")
void testRpcSuccess() {
when(rpcClient.sendTx("tx-002")).thenReturn(true);
assertTrue(multisigService.executeMultisigTx("tx-002", 2));
}
// 补充测试:RPC抛异常分支
@Test
@DisplayName("测试RPC抛异常时返回false")
void testRpcException() {
when(rpcClient.sendTx("tx-003")).thenThrow(new RuntimeException("RPC超时"));
assertFalse(multisigService.executeMultisigTx("tx-003", 2));
}
// 补充测试:getThreshold方法
@Test
@DisplayName("测试获取门限方法")
void testGetThreshold() {
assertEquals(2, multisigService.getThreshold());
}
}
4、执行命令
5、报告生成位置
JaCoCo 会在项目目录下生成报告:
target/
└── site/
└── jacoco/
├── index.html # 核心HTML报告(打开即可可视化查看)
├── jacoco.xml # XML报告(CI/CD用)
└── jacoco.csv # CSV报告(数据分析用)
6、JaCoCo 报告解读
打开 target/site/jacoco/index.html,核心界面分为 3 部分

6.1)概览页(项目级覆盖率)

6.2)类详情页
代码行标注颜色:
✅ 绿色:已覆盖(如 txId.isEmpty() 行);
❌ 红色:未覆盖(如 catch (RuntimeException e) 行、getThreshold() 方法);
⚠️ 黄色:部分覆盖(如 if (signatureCount < threshold) 仅测试了 true 分支?不,本例中测试了 true,false 未测?需看具体)。
6.3)分支详情
if (signatureCount < threshold):true 分支被测试(签名数 = 1),false 分支被测试(签名数 = 2)→ 100% 覆盖;
try/catch:try 块被测试(RPC 成功),catch 块未测试 → 50% 覆盖;
if (txId == null || txId.isEmpty()):true 分支被测试(空 txId),false 分支被测试(非空 txId)→ 100% 覆盖。
7、排除无需覆盖的代码
项目中,部分代码(如配置类、工具类常量)无需统计覆盖率,可在 JaCoCo 中排除。
7.1)JaCoCo 排除的核心场景
代码完全无需统计覆盖率,排除后能让覆盖率指标更精准:
| 排除场景 | 典型示例 |
|---|---|
7.2) 如何配置
(1) 包 / 类级排除
<!-- Maven 配置排除 -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<configuration>
<!-- 核心:排除配置 -->
<excludes>
<!-- 1. 排除整个包(通配符:** 匹配任意子包,* 匹配任意类) -->
<exclude>com/multisig/config/**/*</exclude> <!-- 排除配置包 -->
<exclude>com/multisig/constants/**/*</exclude> <!-- 排除常量包 -->
<!-- 2. 排除指定类(精确匹配) -->
<exclude>com/multisig/MockRpcClient.class</exclude> <!-- 排除Mock类 -->
<!-- 3. 排除匹配规则的类(通配符) -->
<exclude>com/multisig/*DTO.class</exclude> <!-- 排除所有DTO实体类 -->
<exclude>com/multisig/*Utils.class</exclude> <!-- 排除所有工具类 -->
<!-- 4. 排除测试生成代码 -->
<exclude>com/multisig/api/**/*</exclude> <!-- 排除OpenAPI生成的API层 -->
</excludes>
</configuration>
<!-- 其余executions配置不变 -->
</plugin>
配置通配符规则
| 通配符 | 含义 | 示例 | 匹配结果 |
|---|---|---|---|
(2) 类 / 方法级排除(注解方式,精准控制)
JaCoCo 支持通过自定义注解标记需要排除的类 / 方法,无需修改插件配置,这种不推荐。
(3) 单行代码排除(精准过滤单行)
针对方法内无需统计的单行代码(如日志、异常兜底),JaCoCo 支持通过特殊注释排除单行:
在需要排除的代码行末尾添加注释:// $COVERAGE-IGNORE$
public class MultisigService {
public boolean executeMultisigTx(String txId, int signatureCount) {
if (txId == null || txId.isEmpty()) {
return false;
}
try {
return rpcClient.sendTx(txId);
} catch (RuntimeException e) {
// 排除日志行(无需统计覆盖率)
log.error("RPC调用失败", e); // $COVERAGE-IGNORE$
// 排除兜底返回行(简单逻辑,无需测试)
return false; // $COVERAGE-IGNORE$
}
}
}
(4) 报告级排除(仅过滤报告,不影响插桩)
若仅需在覆盖率报告中隐藏某些代码(但仍会插桩统计),可通过报告过滤实现:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<id>jacoco-report</id>
<phase>test</phase>
<goals><goal>report</goal></goals>
<configuration>
<!-- 仅报告中排除,插桩仍会执行 -->
<excludes>
<exclude>com/multisig/utils/**/*</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
学习技术不是用来写HelloWorld和Demo的,而是要用来解决线上系统的真实问题的.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。