测试与性能
测试与性能 · androidx.test
Compose UI Test:测试语义,不追坐标
创建受控内容,通过 semantics 查找、操作和断言节点。
最后更新 2026-08-01
复制即用
@get:Rule
val composeRule = createComposeRule()
@Test
fun counter_increments() {
composeRule.setContent { CounterScreen() }
composeRule.onNodeWithText("增加").performClick()
composeRule.onNodeWithText("计数:1").assertIsDisplayed()
}查找节点
| API | 用途 |
|---|---|
onNodeWithText("...") | 按文本查找 |
onNodeWithContentDescription("...") | 按无障碍描述查找 |
onNodeWithTag("...") | 按 Modifier.testTag 查找 |
onNode(hasTestTag("...") and hasText("...")) | 组合条件 |
onAllNodesWithText("...") | 多个匹配 |
onNodeWithText("...", useUnmergedTree = true) | 未合并语义树 |
操作与断言
composeRule.onNodeWithText("增加").performClick()
composeRule.onNodeWithTag("list").performScrollToIndex(10)
composeRule.onNodeWithText("计数:1").assertIsDisplayed()
composeRule.onNodeWithTag("dialog").assertIsDisplayed()
composeRule.onNodeWithTag("dialog").performClick() // 确认弹窗
composeRule.onAllNodesWithText("item").assertCountEquals(5)
composeRule.onNodeWithText("加载中").assertDoesNotExist()动画与异步
// 控制动画时钟
composeRule.mainClock.autoAdvance = false
composeRule.mainClock.advanceTimeBy(500)
// 等待外部异步
composeRule.waitUntil(timeoutMillis = 5_000) {
composeRule.onAllNodesWithText("完成").fetchSemanticsNodes().isNotEmpty()
}常见陷阱
- 用坐标测试:语义树优先,
performTouchInput { click() }易碎。 Thread.sleep猜时间:用mainClock或waitUntil。- 不清理状态:每个测试独立,
setContent覆盖即可,但外部资源(网络 mock)要重置。 - v1/v2 import 混用:当前源码 samples 用
androidx.compose.ui.test.junit4.v2.createComposeRule(alpha 方向),以所用版本 API 为准,别混用。
要点
- 通过 semantics tree 查找:文本、content description、可访问性角色优先,testTag 兜底。
- 规则等待 Compose 异步进入空闲;外部异步用 idling resource 或
waitUntil。 - 动画用
mainClock控制,不用固定 sleep。 - v2 createComposeRule 是当前源码方向。