测试环境搭建:工欲善其事,必先利其器

说到做 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.x8.0编译测试时莫名报错 Missing class
8.1.x8.2Run configurations 里找不到测试任务
8.2.x ~ 8.4.x8.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")
}
小技巧:我习惯在 Gradle 文件里把版本号抽到 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. 知识结构一览

下面这张图把本章的关键节点串起来了。你可以把它当作搭建检查清单。

测试环境搭建流程 ① 选择 Android Studio ② Gradle & AGP 版本 ③ 添加测试依赖(ui-test-junit4等) ④ 配置测试变体(可选) ⑤ 运行“Hello Test”验证 常见问题 • 依赖版本不匹配 • 混淆导致类丢失 • Build Variants 混乱

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。
一句话总结:环境搭建就像盖房子的地基——看着简单,但每个螺丝都得拧紧。严格按照上面的步骤来,基本一次过。要是卡住了,大概率是版本问题,先检查依赖版本号,再检查 Gradle 缓存。