mcpField
C
configuring-test-dependencies

configuring-test-dependencies

github.com/skydoves测试与质量33962 次安装22 周前更新
代码仓库

关于 configuring-test-dependencies

面向 Android Compose 项目维护者的 Gradle 测试依赖配置技能,说明设备测试与 Robolectric 主机测试所需的依赖及配置位置。它还涵盖测试清单、lint 警告和无障碍检查依赖。

主要功能

  1. 01区分设备测试与主机测试依赖
  2. 02配置 ui-test-manifest 的位置
  3. 03说明 ComponentActivity 启动要求
  4. 04解释 TestManifestGradleConfiguration 警告
  5. 05列出无障碍检查依赖

适用场景

  1. 01修复 Compose 测试无法启动 Activity
  2. 02处理 createComposeRule 无法解析
  3. 03为 Robolectric 补齐测试依赖
  4. 04排查 TestManifestGradleConfiguration 警告

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/skydoves/android-testing-skills --skill configuring-test-dependencies

安装后在 Claude Code 中输入/configuring-test-dependencies确认已加载。

详细介绍

配置测试依赖项——正确设置 Gradle 矩阵

Compose UI 测试在依赖项层面会以三种常见方式失败:JUnit4 接口放在了错误的源代码集、缺少 ui-test-manifest 工件或配置不正确,或者主机测试的三件套不完整。本技能明确规定了依赖矩阵以及用于检查该矩阵的 lint 警告。后续技能(choosing-test-rule-vs-runtest/SKILL.md、setting-up-host-vs-device-tests/SKILL.md)假定依赖项配置正确。

何时使用本技能

  • 用户报告 java.lang.RuntimeException: Could not launch activity within 45 seconds. Examined activities: ... androidx.activity.ComponentActivity 或 ActivityNotFoundException。
  • IDE 将 createComposeRule 标记为未解析,或将 import androidx.compose.ui.test.junit4.createComposeRule 标记为红色。
  • lint 在 build.gradle.kts 的某一行报告 TestManifestGradleConfiguration(严重性为 WARNING)。
  • 用户想为 Compose 添加 Robolectric/主机(JVM)测试,并询问应依赖哪些工件。
  • 用户想启用 enableAccessibilityChecks(...),但不确定它由哪个工件提供。

何时不使用本技能

  • 依赖项已正确配置,用户正在 createComposeRule() 和 runComposeUiTest { } 之间进行选择——请使用 ./choosing-test-rule-vs-runtest/SKILL.md。
  • 依赖项可以编译,但测试运行在错误的源代码集(test/ 而非 androidTest/,或反之)——请使用 ./setting-up-host-vs-device-tests/SKILL.md。
  • 测试可以编译并运行,但存在间歇性失败/超时——请从 ../../synchronization/synchronizing-with-idle/SKILL.md 开始。

前提条件

  • 应用或库已应用 Android Gradle Plugin 的 com.android.application 或 com.android.library 插件。
  • 模块已启用 Compose(应用了 org.jetbrains.kotlin.plugin.compose 插件,或设置了 buildFeatures.compose = true)。
  • 测试类路径中有 JUnit4 依赖(Compose 的 ui-test-junit4 工件仍使用 JUnit4;JUnit5 需要第三方 Vintage 引擎桥接,这不在本技能范围内)。

工作流程

  • 1. 确定模块需要哪些测试接口。 典型的 Compose 功能模块需要三种:用于截图/RenderThread/全栈覆盖的插桩测试、用于快速逻辑检查的主机(Robolectric)测试,以及供 createComposeRule() 使用的清单辅助项。请从第 2 步的矩阵中选择。

  • 2. 在正确的配置中添加依赖项。 将以下内容复制到模块的 build.gradle.kts 中:

dependencies {
    // --- Instrumentation tests (src/androidTest/) ---
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    // MUST be debugImplementation (see Pattern: WRONG vs RIGHT for ui-test-manifest below)
    debugImplementation("androidx.compose.ui:ui-test-manifest")

    // --- Host tests with Robolectric (src/test/) ---
    testImplementation("androidx.compose.ui:ui-test")            // common test API
    testImplementation("androidx.compose.ui:ui-test-junit4")     // ComposeTestRule + createComposeRule
    testImplementation("androidx.compose.ui:ui-test-manifest")   // ComponentActivity manifest entry
    testImplementation("org.robolectric:robolectric:4.13")       // pin a version

    // --- Optional: accessibility validation (API 34+) ---
    androidTestImplementation("androidx.compose.ui:ui-test-junit4-accessibility")
    // Or, when using runComposeUiTest { } (no Rule):
    androidTestImplementation("androidx.compose.ui:ui-test-accessibility")
}

确切的 GA 版本来自 Compose UI BOM 或开发者选择的 composeVersion。Robolectric 需单独指定版本——它不包含在 Compose BOM 中。

  • 3. 如果测试因 androidx.activity.ComponentActivity 出现 ActivityNotFoundException 而失败,99% 的情况下都是清单工件导致的。 该工件是一个单文件库,会将此 <activity> 条目合并到测试 APK 清单中;该条目取自 compose/ui/ui-test-manifest/src/main/AndroidManifest.xml:
<activity android:theme="@android:style/Theme.Material.Light.NoActionBar"
    android:name="androidx.activity.ComponentActivity" android:exported="true" />

没有该条目时,createComposeRule() 和 runComposeUiTest { }(二者默认都会通过 ActivityScenario.launch 启动 androidx.activity.ComponentActivity)都会在启动时崩溃。诊断步骤:

  • 确认依赖列表中确实包含 ui-test-manifest。

  • 确认它位于 debugImplementation 中(主机测试则使用 testImplementation)。如果它位于 androidTestImplementation 中,lint 会发出警告,Gradle 也仍会将其接入——但 AGP 变体模型会有意将 debug 和 androidTest 的清单合并分开处理,而且将其放在 androidTestImplementation 中不会把 <activity> 合并到插桩测试所针对的主机 APK 中。lint 检查正是为发现此问题而设的。

  • 4. 通过将依赖移至 debugImplementation,解决 TestManifestGradleConfiguration lint 警告。 lint 检查位于 compose/ui/ui-test-manifest-lint/src/main/java/androidx/compose/ui/test/manifest/lint/GradleDebugConfigurationDetector.kt。严重级别为 Severity.WARNING,快速修复会将配置名称替换为 debugImplementation。当此构件出现在 implementation、api、compileOnly、runtimeOnly、androidTestImplementation、testImplementation 中,或出现在名称以 android / test / 任一受支持配置名称开头的自定义配置中时,检查会触发。

  • 5. 确定是否确实需要该 manifest 构件。 以下情况需要:

    • 测试使用 createComposeRule()(没有类型参数,默认使用 androidx.activity.ComponentActivity)。
    • 测试使用 runComposeUiTest { }(没有类型参数,默认值相同)。

    以下情况不需要:

    • 测试使用 createAndroidComposeRule<MyActivity>(),且开发者在 src/androidTest/AndroidManifest.xml(或 src/debug/AndroidManifest.xml)中自行声明了 <activity android:name=".MyActivity" .../>。
    • 测试使用 createEmptyComposeRule(),并针对生产 manifest 中已声明的 Activity 启动自己的 ActivityScenario。
  • 6. 对于主机测试,确保所有三个构件都使用 testImplementation。 Robolectric 会驱动 JVM Looper,但 Compose 的空闲状态/时钟实现需要 ui-test(通用)以及 ui-test-junit4(ComposeTestRule 接口和 createComposeRule 实际实现)。如果 ui-test-manifest 未使用 testImplementation,Robolectric 会因解析同一个 manifest 而抛出相同的 ActivityNotFoundException。androidx 本身的主机源集使用此配置,参见 compose/ui/ui-test/src/androidHostTest/kotlin/androidx/compose/ui/test/RobolectricComposeTest.kt。

  • 7. 仅当开发者计划调用 enableAccessibilityChecks 时,才添加无障碍构件。 它们独立于 JUnit4 接口:

    • ui-test-accessibility 扩展 ComposeUiTest(即 runComposeUiTest { } 的接收者)。
    • ui-test-junit4-accessibility 扩展 ComposeTestRule / AndroidComposeTestRule。 两者都标注了 @RequiresApi(34),在 Robolectric 上都会变成空操作(Build.FINGERPRINT == "robolectric" 时会记录警告)——后续处理请参阅此仓库中的无障碍技能说明。
  • 8. 同步 Gradle 并重新运行测试。 执行干净的 ./gradlew :app:connectedDebugAndroidTest(仪器测试)或 ./gradlew :app:testDebugUnitTest(主机测试)后,应能成功编译并找到 ComponentActivity。

常见模式

模式:错误与正确示例 — ui-test-manifest 配置
// WRONG
dependencies {
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    androidTestImplementation("androidx.compose.ui:ui-test-manifest")
}
// WRONG because: lint emits TestManifestGradleConfiguration (WARNING) and the manifest
// merger does not pull the <activity> entry into the merged APK manifest from the
// androidTest source set. createComposeRule() crashes with ActivityNotFoundException.
// RIGHT
dependencies {
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
}

lint 检查明确允许使用 debugImplementation,并会对其他所有配置发出警告。摘自 GradleDebugConfigurationDetector.kt:

"The androidx.compose.ui:ui-test-manifest dependency is needed for launching a
 Compose host, such as with createComposeRule. However, it only needs to be present
 in testing configurations therefore use this dependency with the debugImplementation
 configuration"
模式:错误与正确示例 — 主机测试依赖项
// WRONG
dependencies {
    testImplementation("androidx.compose.ui:ui-test-junit4")
    testImplementation("org.robolectric:robolectric:4.13")
    // Missing ui-test-manifest -> ActivityNotFoundException at runComposeUiTest launch
    // Missing ui-test          -> linker errors on common test API symbols
}
// RIGHT
dependencies {
    testImplementation("androidx.compose.ui:ui-test")
    testImplementation("androidx.compose.ui:ui-test-junit4")
    testImplementation("androidx.compose.ui:ui-test-manifest")
    testImplementation("org.robolectric:robolectric:4.13")
}
模式:错误与正确示例 — 未在自身 manifest 中声明的自定义 Activity
// WRONG
@get:Rule val rule = createAndroidComposeRule<MyHostActivity>()
// build.gradle.kts has only debugImplementation("androidx.compose.ui:ui-test-manifest")
// and src/androidTest/AndroidManifest.xml does NOT declare MyHostActivity.
// WRONG because: ui-test-manifest only declares ComponentActivity. Custom activities
// must be declared in the test APK manifest by the developer. Result: ActivityNotFoundException.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <activity android:name=".MyHostActivity" android:exported="true" />
    </application>
</manifest>
模式:完整示例 build.gradle.kts
plugins {
    id("com.android.library")
    id("org.jetbrains.kotlin.android")
    id("org.jetbrains.kotlin.plugin.compose")
}

android {
    namespace = "com.example.feature"
    compileSdk = 35
    defaultConfig {
        minSdk = 23
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }
    buildFeatures { compose = true }
    testOptions {
        unitTests.isIncludeAndroidResources = true   // required for Robolectric + Compose
    }
}

dependencies {
    val composeBom = platform("androidx.compose:compose-bom:2025.06.00")
    implementation(composeBom)
    androidTestImplementation(composeBom)
    testImplementation(composeBom)

    // Production
    implementation("androidx.compose.ui:ui")
    implementation("androidx.activity:activity-compose")

    // Instrumentation
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
    androidTestImplementation("androidx.test.ext:junit:1.2.1")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")

    // Host (Robolectric)
    testImplementation("androidx.compose.ui:ui-test")
    testImplementation("androidx.compose.ui:ui-test-junit4")
    testImplementation("androidx.compose.ui:ui-test-manifest")
    testImplementation("org.robolectric:robolectric:4.13")
    testImplementation("junit:junit:4.13.2")
}

主机测试 Compose 内容时,必须设置 testOptions.unitTests.isIncludeAndroidResources = true,以便 Robolectric 能读取合并后的资源。

强制规则

  • 必须在 Android 仪器测试中将 androidx.compose.ui:ui-test-manifest 添加到 debugImplementation,在主机测试中添加到 testImplementation。切勿添加到 androidTestImplementation、implementation 或 api。
  • 必须让 ui-test-junit4 与导入 createComposeRule / ComposeTestRule 的测试位于同一源集。在同一模块中同时使用 androidTestImplementation(仪器测试)和 testImplementation(主机测试)没有问题;但若将它们用于错误的测试类,则会导致编译错误。
  • 必须在测试 APK 清单 src/androidTest/AndroidManifest.xml 或 src/debug/AndroidManifest.xml 中声明任何非 ComponentActivity 的测试宿主(自定义 Activity)。ui-test-manifest 工件只声明 androidx.activity.ComponentActivity。
  • 切勿通过 lintOptions { disable("TestManifestGradleConfiguration") } 移除 TestManifestGradleConfiguration lint 检查。请改用快速修复——此检查旨在防止测试静默失败。
  • 切勿将 ui-test-manifest 添加到发布配置(releaseImplementation、implementation)。该工件纯粹是测试脚手架,会让生产 APK 因多出不需要的 <activity> 条目而变大。
  • **建议:**依赖 Compose BOM(androidx.compose:compose-bom),以确保所有 ui-test-* 工件使用协调一致的版本。Robolectric 相互独立,需单独固定版本。
  • **建议:**不确定某个符号属于哪个工件时,请在语料库中搜索 androidx/compose/ui/ui-test* 路径,而不要猜测——参见参考资料。

验证

  • ./gradlew :<module>:connectedDebugAndroidTest 编译并运行成功,且不会因 androidx.activity.ComponentActivity 而出现 ActivityNotFoundException。
  • ./gradlew :<module>:lint 不会报告 TestManifestGradleConfiguration。
  • 当主机测试使用 runComposeUiTest { } 或 createComposeRule() 时,./gradlew :<module>:testDebugUnitTest 可以编译。
  • 依赖代码块清楚地区分 androidTestImplementation(仪器测试)、debugImplementation(清单)和 testImplementation(主机测试三项依赖)。
  • 如果开发者添加了无障碍功能工件,enableAccessibilityChecks(...) 在仪器测试中可以通过编译时解析。
  • 测试 APK 清单中恰好包含一个 <activity android:name="androidx.activity.ComponentActivity"/> 条目(运行 ./gradlew :app:processDebugAndroidTestManifest 并检查 build/intermediates/.../AndroidManifest.xml)。

参考资料

  • Compose 测试设置(Android Developers):https://developer.android.com/develop/ui/compose/testing#setup
  • Compose 测试概览:https://developer.android.com/develop/ui/compose/testing
  • Compose UI 版本说明:https://developer.android.com/jetpack/androidx/releases/compose-ui
  • Compose Multiplatform 测试:https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-test.html
  • compose/ui/ui-test-manifest-lint/src/main/java/androidx/compose/ui/test/manifest/lint/GradleDebugConfigurationDetector.kt — TestManifestGradleConfiguration 问题定义(严重级别为 WARNING,快速修复会改为 debugImplementation)。
  • compose/ui/ui-test-manifest/src/main/AndroidManifest.xml — createComposeRule() 所依赖的合并后 <activity android:name="androidx.activity.ComponentActivity"> 条目。
  • compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt — 默认情况下,runComposeUiTest 会启动 ComponentActivity::class.java(第 184-197 行)。
  • compose/ui/ui-test/src/androidHostTest/kotlin/androidx/compose/ui/test/RobolectricComposeTest.kt — 使用 Robolectric 的标准主机测试类骨架(@RunWith(AndroidJUnit4::class) @Config(minSdk = RobolectricMinSdk))。
  • compose/ui/ui-test/src/androidHostTest/kotlin/androidx/compose/ui/test/Constants.kt — internal const val RobolectricMinSdk = 23。

评论

支持 Markdown