测试环境搭建:工欲善其事,必先利其器
说到做 UI 回归测试,其实最让人头疼的不是写测试代码,而是搭环境。我记得当年第一次在项目里引入 Compose 测试时,光 Gradle 依赖冲突就折腾了大半天。你想想,如果环境没弄对,后面跑测试全是莫名其妙的红,根本分不清是代码问题还是配置问题。所以咱们先把这一步踩稳。
1. 选择 Android Studio 版本
我个人习惯直接用最新稳定版 Android Studio。目前推荐 Hedgehog 或更高版本(2023.1.1+)。为什么?因为低版本对 Compose 的符号解析和测试插桩支持不够好,尤其当你用到 @Preview 和 @Composable 混用测试时,老版本容易出现缓存不刷新的魔幻问题。我曾经在 Arctic Fox 上折腾了两小时,换个版本直接好了。
- 最低要求:Android Studio 2022.3 (Giraffe) 以上
- 推荐:Hedgehog (2023.1.1) 或 Koala (2024.1.1)
- 注意:不要用 Canary 版本做日常回归测试,不稳定。
2. Gradle 与 AGP 版本搭配
这是个经典雷区。我一般打开 gradle-wrapper.properties 看一眼,然后对照官方兼容性表。Compose 测试对 AGP 版本其实没那么挑剔,但 Gradle 版本太低会导致测试任务并行度不够,拖慢回归速度。
| AGP 版本 | 最小 Gradle 版本 | 我踩过的坑 |
|---|---|---|
| 8.0.x | 8.0 | 编译测试时莫名报错 Missing class |
| 8.1.x | 8.2 | Run configurations 里找不到测试任务 |
| 8.2.x ~ 8.4.x | 8.4 ~ 8.7 | 嗯,这个区间我跑得最稳 |
我的做法:在 gradle/wrapper/gradle-wrapper.properties 里固定版本号,别用 latest 或 +。我曾见过同事因为用了动态版本,某天突然全部测试任务都变成 SKIPPED,查了半天是兼容性热更导致的。
3. 添加测试依赖(核心步骤)
说白了就是三行依赖的事。但你知道,Android 项目里容易被翻倍——一个测试在 test 文件夹(单元测试),另一个在 androidTest 文件夹(插桩测试)。回归测试主要走插桩,因为我们要测真实的 UI 渲染和交互。
// build.gradle (模块级)
android {
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
}
dependencies {
// Compose UI 测试核心库
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.6.0")
// 测试工具(可选但强烈建议)
androidTestImplementation("androidx.test:runner:1.5.2")
androidTestImplementation("androidx.test:rules:1.5.0")
// 如果要用 Espresso 辅助断言
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
ext 或 libs.versions.toml 中,这样升级版本时一目了然。另外
ui-test-junit4 的版本必须和 compose-bom 版本对齐,否则会出现 NoSuchMethodError。
4. 配置测试变体与测试任务
默认情况下,androidTest 只编译 debug 变体。如果你想在 release 上跑回归测试(比如做上线前验证),就需要单独配置。我在项目中遇到过这种情况:release 变体开启了混淆,而测试代码里用到的一些 Compose 内部 API 被混淆了,导致测试失败。好家伙,一顿排查才发现是混淆映射没配置测试规则。
android {
testBuildType = "release" // 使用 release 变体进行测试
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
// 保留 Compose 测试需要的类
testProguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'test-proguard-rules.pro'
}
}
}
testBuildType = "release",记得在 proguard-rules.pro 里添加 -keep class androidx.compose.ui.test.** { *; },否则跑测试时会崩。
5. 验证环境:第一个“Hello Test”
环境搭没搭对,写个最小测试跑一遍就知道。我一般会创建一个最简单的 Composable 和一个对应测试,确保能编译通过并成功运行在模拟器上。
// MyScreen.kt
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name")
}
// MyScreenTest.kt (放在 androidTest 目录下)
@RunWith(AndroidJUnit4::class)
class MyScreenTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun greeting_showsCorrectText() {
composeTestRule.setContent {
Greeting("Jetpack")
}
composeTestRule.onNodeWithText("Hello Jetpack").assertExists()
}
}
跑一下:./gradlew connectedDebugAndroidTest。如果出现绿色,恭喜你,环境搞定。如果报红,别慌,八成是 Gradle 缓存问题。我曾经遇到过一个案例:明明依赖都对了,但测试永远提示 Class not found。最后发现是 Android Studio 的 Build Variants 面板里当前选中的是 release,但测试任务用了 debug 变体产生的中间产物,清理一下项目再 Build 就好了。
6. 知识结构一览
下面这张图把本章的关键节点串起来了。你可以把它当作搭建检查清单。
7. 避坑总结
- 版本对齐:ui-test-junit4 的版本必须与 compose-ui 版本一致。我曾经因为只改了 BOM 版本忘了更新测试库,结果
createComposeRule()直接报错。 - 模拟器选择:尽量用 API 31+ 的模拟器镜像,低版本模拟器对 Compose 的渲染支持有 bug,测试结果可能不准。
- Gradle 缓存:如果改了依赖但测试不生效,先跑一次
./gradlew clean再--no-build-cache运行。这个坑我至少踩过三次。 - 多模块项目:如果测试写在单独的
:testing模块里,别忘了在androidTestImplementation上加上implementation project(':your-module'),否则测试代码引用不到 Composable。