Compose UI测试基础:搭建环境,JUnit5与Compose Test Rule
UI测试这事儿,说白了就是个“信心工程”。你想想看,要是改了个文字颜色、调了个边距,心里都没底,那还怎么敢合并代码?我在项目中遇到过不止一次,觉得自己改的没问题,结果一跑测试,crash了。从那以后我养成了一个习惯——先搭好测试环境,再写业务代码。
一、环境搭建:Gradle依赖得配对
Compose的测试库分三块:UI测试核心、JUnit集成、以及调试工具。我个人习惯把它们放在一个测试专用的Module里,当然放在app模块的androidTest也行。关键是要把依赖版本对齐,否则你会看到一堆莫名其妙的NoSuchMethodError。
核心依赖(Compose BOM 2024.02.00 为例)
// 顶层 build.gradle 引入 BOM
dependencies {
implementation platform('androidx.compose:compose-bom:2024.02.00')
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
// 如果你用 JUnit5,再加下面这行
androidTestImplementation 'androidx.compose.ui:ui-test-junit5'
}
注意看,ui-test-manifest是debug依赖。我以前就犯过傻,把它加在release依赖里,结果签名包直接炸了。嗯,这里要强调一下:这个manifest只是为了在调试时能弹出测试Activity,正式版千万不能带。
二、JUnit5 与 Compose Test Rule
JUnit5 和 Compose 的配合其实很简单,核心就是一个 Rule:ComposeTestRule。但问题来了——JUnit4 有 @RunWith(AndroidJUnit4::class),JUnit5 用什么呢?答案是 @ExtendWith(AndroidJUnit5::class)。
下面是一个完整的测试类骨架,我一般会直接复制粘贴到新项目里:
import androidx.compose.ui.test.junit5.createComposeRule
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.Rule
@ExtendWith(AndroidJUnit5::class)
class MyComposeTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun testButtonShowsCorrectText() {
composeTestRule.setContent {
MyButton(text = "点我")
}
composeTestRule
.onNodeWithText("点我")
.assertExists()
}
}
注意 @get:Rule 这个写法,JUnit5 的 Rule 必须放 @get:Rule 上,不然 Compose 不会帮你启动 UI。我曾经有一次就漏了 @get:,结果测试跑完了一堆空指针,查了半天才发现。
三、ComposeTestRule 的两个关键方法
你写测试时,最常用的是这两个:
| 方法 | 作用 | 个人小技巧 |
|---|---|---|
setContent |
把 Composable 渲染到测试 Activity | 我习惯在 @BeforeEach 里调用一次,避免每个测试都重复写 |
onNodeWithText / onNodeWithTag |
查找 UI 节点 | 一定要给关键组件加 Modifier.testTag(),不然容易搜索到多个节点 |
四、避坑指南:我曾经踩过的三个大坑
- 坑一:没有加
ui-test-manifest
跑测试直接报ActivityNotFoundException。记得只在debugImplementation里加。 - 坑二:JUnit5 用了
@RunWith
JUnit5 不用@RunWith,要用@ExtendWith。我当年刚迁移时,照搬 JUnit4 的写法,测试压根不执行。 - 坑三:
setContent写在@Test里而不是@BeforeEach
导致每个测试都重复渲染,速度慢得让人崩溃。后来我写成@BeforeEach,清爽多了。
五、知识图谱:Compose UI 测试环境概览
下面这张图把上面讲的内容串起来,你可以打印出来贴在工位上:
提示:垂直阅读,底层是依赖,中间是规则配置,顶层是测试执行。
六、实际项目中的配置清单
我把平时项目里用的配置整理成了一个清单,方便你检查:
Tip: 我一般把这份清单放在项目 README 里,新同事来了直接抄作业。
- BOM 版本统一 —— 所有 Compose 相关库都用同一个 BOM
- 模块级 build.gradle 加
androidTestImplementation和debugImplementation - 测试类注解 ——
@ExtendWith(AndroidJUnit5::class) - Rule 声明 ——
@get:Rule+createComposeRule() - @BeforeEach 里调用
setContent初始化 UI - 给关键组件加 testTag —— 比如
Modifier.testTag("submit_btn")
你想想看,只要按照这六步来,基本不会出大问题。我刚开始写 Compose 测试时,连 @get:Rule 都写错过,现在模板化之后,效率提高了不止一倍。
七、跑一下试试
环境配好之后,最简单的验证就是写一个空测试:
@Test
fun quickSmokeTest() {
composeTestRule.setContent {
Text("Hello World")
}
composeTestRule.onNodeWithText("Hello World").assertExists()
}
如果绿色通过,恭喜你,环境 ok 了。接下来就可以尽情写 Mock 回放测试了。嗯,这一步看似简单,却是后面所有高级玩法的基础——地基不牢,地动山摇。