You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android无法生成Test与AndroidTest的Jacoco合并覆盖率报告求助

Android本地测试与Espresso测试合并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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 17:54:57