根本原因是jacoco与idea覆盖率引擎互斥:idea直运测试不走maven配置,导致exec文件格式不兼容、路径错误、字节码未插桩;需确认运行方式、关闭idea覆盖率或正确配置agent及pom绑定。

JaCoCo 为什么在 IDEA 里跑不出覆盖率报告
根本原因不是插件没装,而是 jacoco-maven-plugin 和 IDEA 自带的覆盖率引擎(IntelliJ Coverage)默认互斥——IDEA 运行测试时压根不走 Maven 的 JaCoCo 配置,它用自己的探针逻辑,结果生成的 exec 文件格式不兼容、路径找不到、类加载器还可能跳过 instrumented 字节码。
- 确认是否在 IDEA 中右键运行的是
Maven test(走 pom.xml 配置),而不是直接点绿色三角形运行Test类(走 IDEA Coverage) - 如果必须用 IDEA 绿色三角形,需关闭自带覆盖率:菜单 Run → Coverage → Show Coverage Data → 取消勾选 “Enable coverage for tests”,再手动配置 JaCoCo agent 启动参数
- Maven 插件配置里
destFile路径建议写绝对路径或基于${project.build.directory},避免 IDEA 工作目录和 Maven 不一致导致文件写到奇怪位置
mvn jacoco:report 报错 “Can't read execution data file”
执行该目标时报这个错,90% 是因为没先运行测试——jacoco:report 只负责把 .exec 转成 HTML,它不采集数据;真正生成 .exec 的是 jacoco:prepare-agent 或测试执行阶段。
- 必须按顺序执行:
mvn clean test(确保jacoco-maven-plugin的prepare-agentgoal 已绑定到test生命周期) - 检查
pom.xml中插件是否配置了execution绑定:<phase>process-test-classes</phase>或更常见的<phase>test</phase> - 验证
.exec文件是否存在:ls target/jacoco.exec(默认路径),如果没生成,说明 agent 没生效,大概率是 JDK 版本 > 17 且没加--add-opens参数
Java 17+ 下 JaCoCo agent 启动失败或覆盖率归零
JDK 17 开始默认强封装内部 API,而 JaCoCo 早期版本(sun.misc.Unsafe 和反射修改类,会被 JVM 拦住,表现为测试能跑但覆盖率始终为 0,或启动时抛 java.lang.IllegalAccessError。
- 升级 JaCoCo 插件到
0.8.12或更高(Maven 插件版本号不是 JaCoCo Core 版本,注意看官网对应关系) - 在
maven-surefire-plugin中显式添加 JVM 参数:<jvmarg>--add-opens java.base/java.lang=ALL-UNNAMED</jvmarg>,必要时补上java.base/java.util=ALL-UNNAMED - 避免混用不同版本:Maven 插件版本、Core 版本、IDEA 内置 JaCoCo 库版本三者最好统一,尤其 IDEA 2023.2+ 自带 JaCoCo 0.8.11,若项目用 0.8.7 就容易冲突
覆盖率报告里显示 “No sources found” 或源码不显示
HTML 报告打开后只有包结构、没有 Java 文件内容,说明 JaCoCo 找不到源码路径。它不会自动从 classpath 或模块里反编译,必须明确告诉它源码在哪。
- 在
jacoco-maven-plugin的reportexecution 中配置<sourcedirectories></sourcedirectories>,值应为${project.basedir}/src/main/java(多模块项目需列出所有模块的 src) - 如果用了 Lombok,确保
lombok.config中没禁用delombok,否则 JaCoCo 解析的是字节码而非原始注解后代码,行号对不上 - IDEA 里查看覆盖率时,右键报告页面 → “Associate with Sources…”,手动指向本地
src/main/java目录,否则它只认 Maven module 的 source root 定义
最麻烦的其实是多模块聚合报告——jacoco:report-aggregate 会合并所有子模块的 .exec,但每个模块的 sourceDirectory 必须单独配,漏一个,那一块就变灰。别指望父 POM 里统一写一次就能继承过去。










