Android Studio多模块项目无法生成Jacoco报告问题排查
环境信息
- JDK版本:11
- Gradle版本:7.5
- Jacoco版本:0.8.8
问题场景
编写聚合多模块单元测试覆盖率报告的jacocoTotalReport任务时遇到两个问题:
- 加入
doFirst代码块动态收集子模块执行数据时,触发错误:The value for this file collection is final and cannot be changed.
对应代码:task jacocoTotalReport(type: JacocoReport, dependsOn: project.getTasksByName("testDebugUnitTest", true)) { doFirst { subprojects.each { project -> def unitTestResultFile = "${project.buildDir}/jacoco/testDebugUnitTest.exec" if (file(unitTestResultFile).exists() && executionData != null) { executionData.setFrom(unitTestResultFile) } } } } - 移除
doFirst后执行无报错,但无法生成覆盖率报告,对应代码:task jacocoTotalReport(type: JacocoReport, dependsOn: project.getTasksByName("testDebugUnitTest", true)) { subprojects.each { project -> def unitTestResultFile = "${project.buildDir}/jacoco/testDebugUnitTest.exec" if (file(unitTestResultFile).exists() && executionData != null) { executionData.setFrom(unitTestResultFile) } } }
问题原因
doFirst报错原因:Gradle任务执行阶段(doFirst/doLast的执行时机),JacocoReport的executionData文件集合已被固化为不可变状态,此时调用setFrom修改会触发文件集合不可变的错误。- 移除
doFirst无报告原因:配置阶段遍历子模块时,testDebugUnitTest任务还未执行,对应的exec文件尚未生成,file(...).exists()判断为false,导致没有任何执行数据被添加到任务中,自然无法生成报告。
解决方案
利用Gradle延迟配置特性,让框架自动处理文件存在性判断和任务依赖,同时补充生成报告必需的源码、类文件路径配置:
task jacocoTotalReport(type: JacocoReport) { // 依赖所有子模块的debug单元测试任务,确保测试先执行 dependsOn subprojects.testDebugUnitTest // 延迟收集子模块的exec文件,Gradle会在任务执行前解析实际路径 executionData subprojects.files { project -> "${project.buildDir}/jacoco/testDebugUnitTest.exec" } // 指定所有子模块的源码路径,用于映射覆盖率数据 sourceDirectories.from subprojects.sourceSets.main.java.srcDirs // 指定所有子模块的编译类文件路径 classDirectories.from subprojects.sourceSets.main.output // 配置报告输出格式和路径(可按需调整) reports { html.required = true xml.required = true csv.required = false html.outputLocation = layout.buildDirectory.dir("reports/jacoco/total/html") } }
关键说明
- 用
subprojects.files { ... }声明executionData属于延迟配置,会在任务执行前才解析文件路径,此时测试任务已完成,exec文件已存在,无需手动判断。 - 必须配置
sourceDirectories和classDirectories,否则Jacoco无法将执行数据映射到源码,无法生成有效报告。 - 直接依赖
subprojects.testDebugUnitTest比getTasksByName更简洁,确保所有子模块测试任务优先执行。
内容的提问来源于stack exchange,提问作者Dehong Liang
相关产品推荐
相关产品推荐

