Android无法生成Test与AndroidTest的Jacoco合并覆盖率报告求助
问题背景
已实现本地单元测试(Test)和Espresso仪器化测试(AndroidTest),期望生成两者合并的Jacoco代码覆盖率报告,但遇到以下问题:
- 使用
com.hiya.jacoco-android插件时,自定义任务生成的合并报告显示No class files specified - 切换至
org.jacoco:org.jacoco-core后,本地单元测试报告无法生成,且类排除规则未生效
用户提供的Gradle配置如下:
plugins { id 'com.hiya.jacoco-android' } jacoco { toolVersion = "$jacocoVersion" reportsDirectory = file("$buildDir/reports/coverage") } tasks.withType(Test) { jacoco.includeNoLocationClasses = true jacoco.excludes = ['jdk.internal.*'] } task jacocoCombinedTestReports(type: JacocoReport, dependsOn: ['jacocoTestReport', 'createDebugCoverageReport']) { group = "Verification" description = "Creates JaCoCo test coverage report for Unit and Instrumented Tests (combined) on the Debug build" reports { xml.enabled = true html.enabled = true } // Files to exclude: // Generated classes, platform classes, etc. def excludes = [ '**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*', '**/*_Factory*.*', '**/*_Impl.class', '**/*$ViewInjector*', '**/*$Lambda$*', '**/*$inlined$*', '**/*$Synthetic*' ] /*def debugTree = fileTree( dir: "$project.buildDir/tmp/kotlin-classes/debug", excludes: excludes ) def mainSrc = "$project.projectDir/src/main/java" sourceDirectories.from(files([mainSrc])) classDirectories.from(files([debugTree]))*/ // generated classes classDirectories.from = fileTree( dir: "$buildDir/intermediates/classes/debug", excludes: excludes ) + fileTree( dir: "$buildDir/tmp/kotlin-classes/debug", excludes: excludes ) // sources sourceDirectories.from = [ android.sourceSets.main.java.srcDirs, "src/main/kotlin" ] executionData.from = fileTree(dir: project.buildDir, includes: [ 'jacoco/*.exec', 'outputs/code_coverage/debugAndroidTest/connected/**/*.ec' ]) }
问题原因与修复方案
1. 类文件路径硬编码适配问题
Android Gradle插件(AGP)7.0+版本后,Java编译产物路径已从intermediates/classes/debug变更为intermediates/javac/debug/classes,硬编码旧路径会导致找不到类文件,出现No class files specified错误。
修复方式:
避免硬编码路径,直接通过android.sourceSets.main.output.classesDirs自动获取类文件输出目录,同时应用排除规则:
classDirectories.setFrom( android.sourceSets.main.output.classesDirs.collect { dir -> fileTree(dir: dir, excludes: excludes) } )
2. 执行数据文件匹配不准确
原配置中jacoco/*.exec会匹配所有.exec文件,但本地单元测试默认生成的是testDebugUnitTest.exec,宽泛匹配可能引入无关文件;同时仪器化测试的.ec文件路径在部分AGP版本中可能有变化。
修复方式:
精准匹配测试生成的执行文件:
executionData.from = fileTree(dir: project.buildDir, includes: [ 'jacoco/testDebugUnitTest.exec', 'outputs/code_coverage/debugAndroidTest/connected/**/*.ec' ])
3. 依赖任务冗余
原任务依赖jacocoTestReport和createDebugCoverageReport,这两个任务本身已依赖测试执行任务,直接依赖测试执行任务更高效,避免冗余执行。
修复方式:
修改任务依赖为测试执行任务:
task jacocoCombinedTestReports(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'connectedDebugAndroidTest']) { // ... 其他配置 }
4. 类排除规则生效问题
原配置中直接赋值classDirectories.from =可能导致排除规则未正确应用,改用setFrom()方法结合collect遍历目录,确保每个类文件树都应用排除规则。
完整修复后的Gradle配置
plugins { id 'com.android.application' id 'kotlin-android' id 'com.hiya.jacoco-android' } jacoco { toolVersion = "$jacocoVersion" reportsDirectory = file("$buildDir/reports/coverage") } tasks.withType(Test) { jacoco.includeNoLocationClasses = true jacoco.excludes = ['jdk.internal.*'] } task jacocoCombinedTestReports(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'connectedDebugAndroidTest']) { group = "Verification" description = "Creates JaCoCo test coverage report for Unit and Instrumented Tests (combined) on the Debug build" reports { xml.enabled = true html.enabled = true } // Files to exclude: Generated classes, platform classes, etc. def excludes = [ '**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*', '**/*_Factory*.*', '**/*_Impl.class', '**/*$ViewInjector*', '**/*$Lambda$*', '**/*$inlined$*', '**/*$Synthetic*' ] // 自动适配AGP版本的类文件路径,应用排除规则 classDirectories.setFrom( android.sourceSets.main.output.classesDirs.collect { dir -> fileTree(dir: dir, excludes: excludes) } ) // 源代码路径,自动包含Java和Kotlin源码目录 sourceDirectories.setFrom(android.sourceSets.main.java.srcDirs + android.sourceSets.main.kotlin.srcDirs) // 执行数据文件,精准匹配测试生成的文件 executionData.from = fileTree(dir: project.buildDir, includes: [ 'jacoco/testDebugUnitTest.exec', 'outputs/code_coverage/debugAndroidTest/connected/**/*.ec' ]) }
执行命令
运行前先清理构建缓存,避免旧文件干扰:
./gradlew clean jacocoCombinedTestReports
内容的提问来源于stack exchange,提问作者Chetan Patil

