Android多产品风味Kotlin+Hilt项目JaCoCo报错覆盖率为0求助
问题根因
字节码转换导致类不匹配的判断方向正确,但类文件拷贝时机完全错误。
Hilt字节码插桩、Kotlin协程内部类生成、AGP自身的类转换(desugar、多flavor变体类合并、debug版字节码埋点等)都不会在compileDebugJavaWithJavac任务结束时终止,你在Hilt任务执行前拷贝的类属于插桩前的中间产物,和测试运行时实际加载的插桩后类结构完全不一致,用中间产物匹配运行时生成的覆盖率exec数据,必然报类不匹配错误,最终覆盖率统计为0。
另外原有配置的Hilt任务匹配正则存在覆盖漏洞,没有命中Hilt ASM插桩、Kotlin类转换的核心任务,依赖顺序配置完全失效。
解决方案
不需要自定义任务提前拷贝类文件,直接读取所有字节码转换完成后的最终类产物,按变体维度配置JaCoCo任务即可,适配多product flavor场景:
- 修正类文件读取路径,废弃自定义的
copyClassFilesForJacoco任务,直接指向AGP输出的全量插桩后的类目录:- AGP 7.0及以上版本路径:
${buildDir}/intermediates/asm-instrumented/classes/${变体名}/ - AGP 4.x版本路径:
${buildDir}/intermediates/transforms/HiltTransform/${变体名}/
注意不要重复读取javac、kotlin编译任务输出的中间类目录,避免类冲突。
- AGP 7.0及以上版本路径:
- 遍历所有构建变体自动生成对应JaCoCo任务,不要硬编码单个flavor的路径,参考配置如下:
android.applicationVariants.all { variant -> def variantName = variant.name.capitalize() def unitTestTask = tasks.getByName("test${variantName}UnitTest") tasks.register("jacoco${variantName}CoverageReport", JacocoReport) { dependsOn unitTestTask group = "verification" description = "Generate Jacoco coverage report for ${variant.name}" // 配置全量插桩后的最终类目录,过滤不需要统计的自动生成类 classDirectories.setFrom(files(fileTree( dir: "${buildDir}/intermediates/asm-instrumented/classes/${variant.name}", excludes: [ '**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/hilt_aggregated_deps/**', '**/*_Factory.class', '**/*_MembersInjector.class', '**/Hilt_*.class', '**/*$Lambda$*.*' ] ))) // 配置源码目录,覆盖main和对应flavor的java、kotlin路径 sourceDirectories.setFrom(files([ "${project.projectDir}/src/main/java", "${project.projectDir}/src/main/kotlin", "${project.projectDir}/src/${variant.flavorName}/java", "${project.projectDir}/src/${variant.flavorName}/kotlin" ])) // 配置单元测试生成的覆盖率数据路径 executionData.setFrom(fileTree( dir: buildDir, includes: ["jacoco/${unitTestTask.name}.exec"] )) reports { xml.required.set(true) html.required.set(true) } } } - 补充避坑配置:
- 在testOptions中排除Hilt自动生成类的覆盖率统计,避免无效数据干扰:
testOptions { unitTests.all { jacoco { excludeJdkClasses = true excludes = ['**/*hilt**', '**/*Hilt*', '**/*_MembersInjector*', '**/*_Factory*'] } } } - 如果debug构建类型开启了混淆,必须在debug混淆规则中添加
-dontobfuscate和-keepattributes *,否则混淆后的类同样会和覆盖率数据不匹配。 - 报错中提到的协程生成内部类
Data$callApi$1、Hilt生成工厂类SomeFactory$SomeGenerator如果不需要纳入统计,可以直接加到classDirectories的excludes列表中过滤,不影响业务代码覆盖率准确率。
- 在testOptions中排除Hilt自动生成类的覆盖率统计,避免无效数据干扰:
验证方式
先执行./gradlew clean清空历史构建产物,再执行对应变体的JaCoCo任务,例如flavorADebug变体对应执行./gradlew jacocoFlavorADebugCoverageReport,任务执行完成后不会再出现类不匹配的报错,覆盖率数据可正常生成。
内容的提问来源于stack exchange,提问作者NightFury
相关产品推荐
相关产品推荐

