Compose UI Test:测试语义,不追坐标 · AndroidX 源码指南
AAndroidX 源码指南
测试与性能
测试与性能 · 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 猜时间:用 mainClockwaitUntil
  • 不清理状态:每个测试独立,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 是当前源码方向。

相关页面