关于 configuring-test-dependencies
面向 Android Compose 项目维护者的 Gradle 测试依赖配置技能,说明设备测试与 Robolectric 主机测试所需的依赖及配置位置。它还涵盖测试清单、lint 警告和无障碍检查依赖。
主要功能
- 01区分设备测试与主机测试依赖
- 02配置 ui-test-manifest 的位置
- 03说明 ComponentActivity 启动要求
- 04解释 TestManifestGradleConfiguration 警告
- 05列出无障碍检查依赖
适用场景
- 01修复 Compose 测试无法启动 Activity
- 02处理 createComposeRule 无法解析
- 03为 Robolectric 补齐测试依赖
- 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,解决TestManifestGradleConfigurationlint 警告。 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") }移除TestManifestGradleConfigurationlint 检查。请改用快速修复——此检查旨在防止测试静默失败。 - 切勿将
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。