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

如何将Android多模块单元测试报告合并到单一目录?

多模块Android单元测试报告合并优化方案

问题背景

为便于将测试报告复制到CI环境,需要把多模块的Android单元测试报告合并到单一目录。由于Gradle的test-report-aggregation插件对Android项目无效,自行编写了项目级build.gradle.kts代码,但存在缺陷:当仅执行app:debugUnitTest时,会自动运行其他模块(如module1)的测试,希望仅合并实际被执行的测试报告。

原项目级build.gradle.kts代码

val debugTestReport: TaskProvider<TestReport> =
    tasks.register<TestReport>("debugTestReport") {
        destinationDirectory.set(file("$buildDir/reports/debug"))

        // Combine all 'test' task results into a single HTML report
        @Suppress("DEPRECATION")
        reportOn(subprojects.map {
            it.tasks.withType(Test::class).filter { test -> test.name == "testDebugUnitTest" }
        }
        )
    }

val releaseTestReport: TaskProvider<TestReport> =
    tasks.register<TestReport>("releaseTestReport") {
        destinationDirectory.set(file("$buildDir/reports/release"))

        // Combine all 'test' task results into a single HTML report
        @Suppress("DEPRECATION")
        reportOn(subprojects.map {
            it.tasks.withType(Test::class).filter { test -> test.name == "testReleaseUnitTest" }
        })
    }

tasks.register<TestReport>("testReport") {
    dependsOn(debugTestReport, releaseTestReport)
}

subprojects {
    project.plugins.configure(project = project)
    // Don't create the reports if we're running from the IDE, ideally we'd only generate the
    // reports for complete test runs anyway
    if (properties.containsKey("android.injected.invoked.from.ide")) return@subprojects
    // Check that we're not running verifyPaparazziDebug or recordPaparazziDebug
    if (gradle.startParameter.taskNames.none { it.matches(Regex(".*[v|r][a-z]*P[a-z]*D.*")) }) {
        tasks.withType<Test> {
            val reports =
                if (name == "testDebugUnitTest") debugTestReport else releaseTestReport
            finalizedBy(reports)
            ignoreFailures = true
            testLogging {
                events("failed")
            }
            exclude("**/design/snapshot/**")
        }
    }
}

优化方案

核心思路是:让合并报告任务仅依赖实际被执行的测试任务,通过Gradle任务图的执行状态动态收集需要合并的测试结果,避免触发未指定的模块测试。

优化后的项目级build.gradle.kts代码

// 注册debug测试报告合并任务,初始不绑定测试任务
val debugTestReport = tasks.register<TestReport>("debugTestReport") {
    destinationDirectory.set(file("$buildDir/reports/debug"))
}

// 注册release测试报告合并任务,初始不绑定测试任务
val releaseTestReport = tasks.register<TestReport>("releaseTestReport") {
    destinationDirectory.set(file("$buildDir/reports/release"))
}

// 统一触发合并任务的入口
tasks.register("testReport") {
    dependsOn(debugTestReport, releaseTestReport)
}

subprojects {
    // IDE环境下跳过报告合并逻辑
    if (properties.containsKey("android.injected.invoked.from.ide")) return@subprojects
    // 排除Paparazzi相关任务场景
    if (gradle.startParameter.taskNames.any { it.matches(Regex(".*[v|r][a-z]*P[a-z]*D.*")) }) return@subprojects

    tasks.withType<Test>().configureEach {
        val currentTestTask = this
        val targetReportTask = if (name == "testDebugUnitTest") debugTestReport else releaseTestReport

        // 监听任务图就绪状态,仅当当前测试任务会被执行时,才关联合并报告任务
        gradle.taskGraph.whenReady { graph ->
            if (graph.hasTask(currentTestTask)) {
                targetReportTask.configure {
                    @Suppress("DEPRECATION")
                    reportOn(currentTestTask)
                }
                // 测试任务执行完成后触发对应合并报告任务
                currentTestTask.finalizedBy(targetReportTask)
            }
        }

        ignoreFailures = true
        testLogging {
            events("failed")
        }
        exclude("**/design/snapshot/**")
    }
}

关键改进点

  • 动态关联任务:通过gradle.taskGraph.whenReady监听任务执行计划,只有当测试任务确实会被执行时,才将其加入合并报告的数据源,并绑定finalizedBy关系,避免触发未指定的模块测试。
  • 移除固定依赖:原代码中reportOn直接绑定所有子模块的测试任务,导致执行单个模块测试时会触发全量测试,优化后仅关联实际运行的测试任务。
  • 逻辑简化:简化子模块任务配置逻辑,确保只有必要的任务参与报告合并流程。

验证方式

  • 执行./gradlew app:debugUnitTest:仅运行app模块的debug单元测试,合并报告仅包含app模块的测试结果,不会触发其他模块测试。
  • 执行./gradlew debugUnitTest:运行所有模块的debug单元测试,合并报告包含所有模块的测试结果。
  • 执行./gradlew testReport:触发debug和release的报告合并(前提是对应测试任务已执行,或会自动触发执行)。

内容的提问来源于stack exchange,提问作者Barry Irvine

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 04:14:52