2. 测试环境搭建:配置 build.gradle 依赖、创建测试目录、初始化 ComposeTestRule
2.1 章节概述
搭建可重复的测试环境是 Compose UI 自动化验证的基石。本章从 Gradle 依赖声明、Android 测试目录结构、ComposeTestRule 初始化三个维度,构建一套标准化的测试底座。完成本章后,你将得到一个可直接运行 Compose UI 测试的工程框架,并理解每个配置项的作用域与原理。
目标状态:在 androidTest 源集下成功运行第一个 Compose 测试用例,通过 ComposeTestRule 完成组件查找与交互。
2.2 原理:测试环境的三层依赖关系
Compose UI 测试依赖链可简化为三层:测试框架层(JUnit 4/5)、Compose 测试库层(compose-ui-test)、设备/模拟器运行时层(AndroidX Test Orchestrator 或 Instrumentation)。三层之间通过 Gradle 配置与测试规则(TestRule)串联。
- 框架层:提供测试生命周期管理、断言、参数化等基础能力。
- Compose 测试库层:提供 ComposeTestRule、Semantics 节点树、手势模拟等 UI 专用 API。
- 运行时层:控制测试 APK 与目标 APK 的隔离策略、截图存储等系统级行为。
2.3 配置 build.gradle 依赖
以下为完整且经过版本对齐的依赖配置(以模块级 build.gradle.kts 为例)。请根据项目实际使用的 Compose BOM 版本调整数字。
// 模块级 build.gradle.kts
android {
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
// 如需截图测试,可打开以下选项
// testInstrumentationRunnerArguments["clearPackageData"] = "true"
}
// 确保测试源集使用 Compose 编译
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.8" // 与 Kotlin 版本对齐
}
}
dependencies {
// 核心 Compose UI 测试库
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.5.4")
// 调试阶段需要的语义树检查工具(debugImplementation 避免泄露到 Release)
debugImplementation("androidx.compose.ui:ui-test-manifest:1.5.4")
// AndroidX Test 基础库
androidTestImplementation("androidx.test:core:1.5.0")
androidTestImplementation("androidx.test:runner:1.5.2")
androidTestImplementation("androidx.test:rules:1.5.0")
// 可选:使用 Orchestrator 实现测试隔离(每次测试后清除状态)
androidTestUtil("androidx.test:orchestrator:1.4.2")
// JUnit 4
androidTestImplementation("junit:junit:4.13.2")
// 如果需要 Espresso 与 Compose 混编
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.test.espresso:espresso-intents:3.5.1")
// 可选:Compose UI 测试的断言增强
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.5.4")
}
版本对齐检查:compose BOM 版本、Kotlin 编译器扩展版本、ui-test-junit4 版本必须来自同一个 Compose 发行版。混用不同发行版会导致 NoClassDefFoundError 或运行时语义树不可用。
2.4 创建测试目录
Android Gradle 插件默认识别两个测试源集:src/test/(单元测试,运行在 JVM)和 src/androidTest/(插桩测试,运行在设备/模拟器)。Compose UI 测试必须位于 androidTest 目录下。
app/
├── src/
│ ├── main/
│ │ ├── java/com/example/app/
│ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── test/
│ │ └── java/com/example/app/
│ │ └── ExampleUnitTest.kt # 纯 JVM 测试
│ └── androidTest/
│ └── java/com/example/app/
│ ├── ComposeTestRuleInitTest.kt # 本章关注的文件
│ └── ui/
│ └── LoginScreenTest.kt # 后续章节示例
└── build.gradle.kts
2.5 初始化 ComposeTestRule
ComposeTestRule 是测试与 Compose UI 交互的入口。标准初始化模式有两种:createAndroidComposeRule(绑定 Activity)与 createComposeRule(不绑定 Activity,完全由测试代码控制 content)。
| 规则类型 | 适用场景 | 生命周期管理 |
|---|---|---|
createAndroidComposeRule<MainActivity>() |
测试完整 Activity 启动、导航、系统回调 | 自动管理 Activity 创建/恢复/销毁 |
createComposeRule() |
测试独立 Composable 组件,无需 Activity 上下文 | 通过 setContent { … } 手动控制 |
// ComposeTestRuleInitTest.kt
package com.example.app
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ComposeTestRuleInitTest {
// 方式 A:绑定真实 Activity
@get:Rule
val activityRule: ComposeTestRule = createAndroidComposeRule<MainActivity>()
// 方式 B:轻量级 Composable 测试
@get:Rule
val composeRule: ComposeTestRule = createComposeRule()
// 用于方式 B 的测试 Composable
@Composable
private fun HelloWorld() {
Text(text = "Hello Compose UI Test!")
}
@Test
fun testActivityRule_findsGreeting() {
// activityRule 的 setContent 由 Activity 的 onCreate 自动触发
activityRule.onNodeWithText("Compose UI Test").assertExists()
}
@Test
fun testComposeRule_manuallySetContent() {
composeRule.setContent {
HelloWorld()
}
composeRule.onNodeWithText("Hello Compose UI Test!").assertExists()
}
}
2.6 调试:验证环境是否就绪
运行上述测试,若绿色通过,则环境搭建成功。常见失败现象与排查路径:
- 测试类不执行:检查
testInstrumentationRunner是否配置为AndroidJUnitRunner。 - NoClassDefFoundError: ComposeTestRule:ui-test-junit4 依赖未能解析;检查 Gradle 缓存或版本冲突。
- ActivityNotFoundException:
createAndroidComposeRule<WrongActivity>指向的 Activity 未在 Manifest 中注册。 - 节点查找超时:setContent 未调用或 Compose 重组未触发;确保测试方法不早于 setContent 完成。
2.7 流程图:测试环境搭建步骤
2.8 性能与稳定性优化
- 依赖精简:仅引入实际需要的测试库,避免 ui-test-manifest 泄漏到 Release 构建(使用 debugImplementation)。
- Orchestrator 加速:启用
testInstrumentationRunnerArguments["clearPackageData"] = "true"后,每次测试间自动清除数据,减少状态污染导致的假阳性失败。 - 规则复用:将 ComposeTestRule 提取到抽象测试基类,子类直接继承,避免重复 @Rule 声明。
- 异步处理:ComposeTestRule 默认使用
IdlingResource等待动画和协程完成;若测试中启动非 Compose 协程,需手动注册或等待。
⚠ 生产环境注意:不要在 Release 构建中包含 androidTestImplementation 依赖;使用
debugImplementation 声明 ui-test-manifest,否则 APK 体积会显著增大,且可能暴露测试接口。
2.9 部署检查清单
- 确认 build.gradle.kts 中 compose 相关依赖版本全部对齐。
- 执行
./gradlew :app:connectedDebugAndroidTest触发测试。 - 观察控制台输出,确认没有 ClassNotFoundException 或 LinkageError。
- 验证 ComposeTestRule 的 setContent 能在 Activity 生命周期中正常触发重组。
- 记录首次测试通过的时间基线,用于后续章节参考。
2.10 常见错误汇总
| 错误信息 | 根因 | 解决 |
|---|---|---|
java.lang.NoClassDefFoundError: androidx/compose/ui/test/junit4/AndroidComposeTestRule |
ui-test-junit4 依赖缺失或版本与 Compose 运行时不一致 | 检查 BOM 版本,同步后 Clean Project |
android.content.ActivityNotFoundException: Unable to find explicit activity class |
createAndroidComposeRule 的 Activity 未在 AndroidManifest.xml 中声明 | 在 <application> 中添加对应 <activity> 标签 |
java.lang.IllegalStateException: ComposeTestRule not initialized. Did you forget to call setContent? |
通过 createComposeRule() 创建后未在测试中调用 setContent | 在 @Before 或第一个 @Test 中调用 composeRule.setContent { ... } |
java.lang.RuntimeException: Failed to start Instrumentation |
testInstrumentationRunner 配置错误或与依赖冲突 | 设置为 androidx.test.runner.AndroidJUnitRunner,移除冲突 runner |
Gradle sync failed: dependency version conflict |
多个 Composable 库版本不一致 | 统一使用 Compose BOM 管理所有版本 |