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 流程图:测试环境搭建步骤

Compose UI 测试环境搭建流程 配置 build.gradle 依赖 Gradle Sync 同步 创建 androidTest 测试目录 初始化 ComposeTestRule 编写 onNode+assert 测试 运行测试 → 绿色通过 ⚠ 常见踩坑点 • 依赖版本与 compose BOM 不匹配 • androidTest 目录创建后未 Gradle Sync • ComposeTestRule 使用 @get:Rule • setContent 放在 @Before 或测试方法中 • Activity 未在 Manifest 中声明 ⚡ 环境稳定性优化 • 使用 Orchestrator 清除测试间状态 • 避免在 @Before 中启动过多协程 • 截图测试目录预先创建并赋予权限

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 部署检查清单

  1. 确认 build.gradle.kts 中 compose 相关依赖版本全部对齐。
  2. 执行 ./gradlew :app:connectedDebugAndroidTest 触发测试。
  3. 观察控制台输出,确认没有 ClassNotFoundException 或 LinkageError。
  4. 验证 ComposeTestRule 的 setContent 能在 Activity 生命周期中正常触发重组。
  5. 记录首次测试通过的时间基线,用于后续章节参考。

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 管理所有版本