用途
本技能用于为 Jetpack Compose UI 测试配置正确的 Gradle 依赖矩阵。Compose UI 测试在依赖层通常会以三种可预测的方式失败:JUnit4 接口被放在了错误的源集、ui-test-manifest 构件缺失或放在错误的配置上、或主机测试(host test)所需三件套不完整。该技能给出精确的依赖矩阵以及监督它的 lint 警告。后续技能(choosing-test-rule-vs-runtest、setting-up-host-vs-device-tests)都假定依赖已经正确。
适用时机
- 用户报告
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。 - 依赖能编译,但测试运行在错误的源集(test/ 与 androidTest/)——使用 setting-up-host-vs-device-tests。
- 测试能编译运行但不稳定/超时——从 synchronizing-with-idle 开始。
前置条件
- 应用了
com.android.application或com.android.library插件的 Android Gradle Plugin。 - 启用 Compose 的模块(
org.jetbrains.kotlin.plugin.compose插件或buildFeatures.compose = true)。 - 测试类路径上存在 JUnit4 依赖(Compose 的 ui-test-junit4 仍使用 JUnit4;JUnit5 需要第三方 Vintage engine 桥接,不在范围内)。
主要能力与工作流程
- 判断模块需要哪些测试面。典型 Compose 功能模块需要三个:用于截图/渲染线程/全栈覆盖的仪器测试、用于快速逻辑检查的主机(Robolectric)测试、以及供
createComposeRule()使用的 manifest 辅助构件。 - 在正确的配置上添加依赖。示例:
dependencies {
// --- Instrumentation tests (src/androidTest/) ---
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
// MUST be debugImplementation
debugImplementation("androidx.compose.ui:ui-test-manifest")
// --- Host tests with Robolectric (src/test/) ---
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")
// --- 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 发布。
- 如果测试因
androidx.activity.ComponentActivity的ActivityNotFoundException失败,几乎可以确定是 manifest 构件问题。该构件会把一个<activity>条目合并进测试 APK 的 manifest:
<activity android:theme="@android:style/Theme.Material.Light.NoActionBar"
android:name="androidx.activity.ComponentActivity" android:exported="true" />
没有该条目,createComposeRule() 与 runComposeUiTest { } 会在启动时崩溃。诊断:确认 ui-test-manifest 是否在依赖列表中;确认它是否在 debugImplementation(主机测试为 testImplementation)。若放在 androidTestImplementation,lint 会告警,且不会把 <activity> 合并进仪器测试运行所针对的宿主 APK。
-
通过把依赖移到 debugImplementation 来处理
TestManifestGradleConfigurationlint 警告。该检查会在构件出现在 implementation、api、compileOnly、runtimeOnly、androidTestImplementation、testImplementation 等配置时触发,严重级别 WARNING,快速修复会替换为 debugImplementation。 -
判断是否需要 manifest 构件。
createComposeRule()(无类型参数)与runComposeUiTest { }(无类型参数)需要它;当使用createAndroidComposeRule<MyActivity>()且自行在测试 manifest 中声明 Activity、或使用createEmptyComposeRule()并对已声明 Activity 启动 ActivityScenario 时,不需要它。 -
对主机测试,确保三个构件都在 testImplementation。缺少 ui-test-manifest 会抛同样的 ActivityNotFoundException;缺少 ui-test 会出现常见测试 API 符号的链接错误。
-
仅在计划调用
enableAccessibilityChecks时添加无障碍构件:ui-test-accessibility扩展 ComposeUiTest;ui-test-junit4-accessibility扩展 ComposeTestRule / AndroidComposeTestRule。两者均@RequiresApi(34),在 Robolectric 上为空操作。 -
同步 Gradle 并重新运行测试。
./gradlew :app:connectedDebugAndroidTest(仪器)或./gradlew :app:testDebugUnitTest(主机)应能编译并定位到 ComponentActivity。
正误对比模式
ui-test-manifest 配置:
// WRONG
androidTestImplementation("androidx.compose.ui:ui-test-manifest")
// RIGHT
debugImplementation("androidx.compose.ui:ui-test-manifest")
主机测试依赖:
// WRONG:缺少 ui-test-manifest(导致 ActivityNotFoundException)与 ui-test(链接错误)
// RIGHT
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")
自定义 Activity 需在 src/androidTest/AndroidManifest.xml 中自行声明:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity android:name=".MyHostActivity" android:exported="true" />
</application>
</manifest>
强制规则
- 必须把
androidx.compose.ui:ui-test-manifest放在 debugImplementation(仪器测试)或 testImplementation(主机测试),绝不放 androidTestImplementation、implementation 或 api。 - 必须让 ui-test-junit4 与导入 createComposeRule / ComposeTestRule 的测试处于同一源集。
- 必须在测试 APK manifest(
src/androidTest/AndroidManifest.xml或src/debug/AndroidManifest.xml)中声明任何非 ComponentActivity 的测试宿主。 - 不得通过
lintOptions { disable("TestManifestGradleConfiguration") }移除该 lint 检查,应应用快速修复。 - 不得把 ui-test-manifest 放在 release 配置上,它是纯测试脚手架,会给生产 APK 增加不需要的
<activity>条目。 - 推荐使用 Compose BOM 使所有 ui-test-* 构件版本一致;Robolectric 独立固定版本。
验证方法
./gradlew :<module>:connectedDebugAndroidTest编译运行,不出现 ComponentActivity 的 ActivityNotFoundException。./gradlew :<module>:lint不报告 TestManifestGradleConfiguration。./gradlew :<module>:testDebugUnitTest在使用runComposeUiTest { }或createComposeRule()时能编译。- 依赖块可见地分离 androidTestImplementation、debugImplementation、testImplementation。
- 测试 APK manifest 中恰好包含一个
<activity android:name="androidx.activity.ComponentActivity"/>条目。
参考资料
- Compose testing setup: https://developer.android.com/develop/ui/compose/testing#setup
- Compose testing overview: https://developer.android.com/develop/ui/compose/testing
- Compose UI release notes: https://developer.android.com/jetpack/androidx/releases/compose-ui
- Compose Multiplatform testing: https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-test.html