SuperAPI AI 工具集

clicking-and-scrolling

测试与质量

查看项目

更新于 10-16 03:00

技能介绍

用途

本技能用于在 Jetpack Compose 测试中,通过高层动作 API 驱动 UI,而不需要经过手势构建器(gesture builder)。这些高层动作会解析为平台相应的原语,或解析为 composable 已经暴露的语义动作。当测试不需要对像素级手势进行控制时,应优先使用这些高层动作;只有当高层动作无法表达意图时,才使用 performTouchInput { … }。

主要能力

  • 点击节点:performClick()。在 Android 上(设备测试与 Robolectric 宿主测试)会委托给 performTouchInput { click() }。
  • 在断言前显示内容节点:performScrollTo()。向上遍历树,找到最近的带有 SemanticsActions.ScrollBy 的父节点,并以最小增量滚动,使节点完全进入视口;若不存在滚动父节点则抛出 AssertionError。
  • 将惰性容器滚动到指定索引:performScrollToIndex(index)。要求接收节点具有 SemanticsActions.ScrollToIndex,接收者必须是容器本身,而不是其中的条目。
  • 滚动到带 key 的条目:performScrollToKey(key)。要求同时具有 IndexForKey 与 ScrollToIndex,匹配 LazyColumn / LazyRow 条目的 key = { … } 参数。
  • 滚动直到匹配器命中:performScrollToNode(matcher)。对惰性容器逐视口从起点走到终点;对非惰性可滚动容器,在节点出现后回退为 performScrollTo;到达内容末尾仍未匹配则抛出异常。
  • 将焦点移动到节点:requestFocus()。是 performSemanticsAction(SemanticsActions.RequestFocus) 的语法糖,在 performIndirectPointerInput 之前必须调用。
  • 调用自定义 AccessibilityAction<T>:performSemanticsAction(key, invocation) 用于带参数动作,performSemanticsAction(key) 用于无参动作。该动作必须通过 Modifier.semantics { … } 声明在节点上,否则抛出 AssertionError。
  • 点击 Text 内的 LinkAnnotation:performFirstLinkClick(predicate)。默认谓词 { true } 点击第一个链接;当接收者没有文本或没有链接满足谓词时抛出异常。

使用方式

选择正确的接收者节点是最常见的失败原因。performClick / performScrollTo / performFirstLinkClick 在内容节点上运行;performScrollToIndex / performScrollToKey / performScrollToNode 在可滚动容器上运行。

点击节点示例:

rule.onNodeWithTag(SubmitButtonTag).performClick()

在断言前显示内容节点示例:

rule.onNodeWithTag(LastItemTag).performScrollTo().assertIsDisplayed()

将惰性容器滚动到索引示例:

rule.onNode(hasScrollToIndexAction()).performScrollToIndex(42)

滚动到带 key 的条目示例:

rule.onNode(hasScrollToKeyAction()).performScrollToKey("user-7")

滚动直到匹配器命中示例:

rule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText("End of feed"))

移动焦点示例:

rule.onNodeWithTag(InputTag).requestFocus()

调用自定义语义动作示例:

val MyAction = SemanticsPropertyKey<AccessibilityAction<(Int) -> Boolean>>("MyAction") // production: Modifier.semantics { this[MyAction] = AccessibilityAction("Bump") { delta -> bump(delta); true } } // test: rule.onNodeWithTag(Tag).performSemanticsAction(MyAction) { it(3) }

点击 Text 内第一个链接示例:

rule.onNodeWithTag(BodyTag).performFirstLinkClick { it.item is LinkAnnotation.Url }

适用场景

  • 开发者询问如何从测试中点击 Button / IconButton / clickable Box。
  • 测试必须在断言前显示某个节点(例如当前位于折叠线以下的条目)。
  • 测试必须将 LazyColumn / LazyRow 滚动到特定索引、key 或匹配的条目。
  • 测试必须触发焦点变化,或调用通过 Modifier.semantics { … } 声明的 AccessibilityAction<T>。
  • 测试必须点击 Text 内的 LinkAnnotation。

限制

  • 当测试需要部分手势、多点触控、速度控制的快速滑动,或跨重组拆分触摸序列时,不应使用本技能,而应使用注入触摸手势相关技能。
  • 当测试涉及悬停、滚轮或右键流程时,不应使用本技能,而应使用注入鼠标与键盘相关技能。
  • 当测试需要输入文本或触发 IME 动作时,不应使用本技能,而应使用输入文本相关技能。
  • 若节点查找持续失败,应先通过按标签、文本、内容查找节点以及打印语义树相关技能验证匹配器。
  • performScrollToIndex / performScrollToKey 要求容器实现相关语义动作;LazyColumn / LazyRow 支持,而普通的 Modifier.verticalScroll 不支持。
  • performFirstLinkClick 要求 Text 使用携带 LinkAnnotation 的 AnnotatedString。
  • 不得对 LazyColumn 条目调用 performScrollTo,因为该条目可能尚未被组合。
  • 不得依赖 performClick 处理悬停、右键或滚轮滚动。

前置条件

  • 按配置测试依赖相关技能配置 androidx.compose.ui:ui-test-junit4(或用于 runComposeUiTest 的 androidx.compose.ui:ui-test)。
  • 目标节点在生产源码中带有 Modifier.testTag("…")。
  • 对于 performScrollToIndex / performScrollToKey:容器必须实现相关语义动作。
  • 对于 performFirstLinkClick:Text 必须使用携带 LinkAnnotation 的 AnnotatedString。

验证要点

  • 每次 performScrollToIndex / performScrollToKey / performScrollToNode 调用都指向容器而非条目,并通过标签或 hasScrollToIndexAction() 匹配器选择。
  • 没有使用 performClick 模拟悬停、右键或滚轮。
  • 每次 performSemanticsAction 调用都引用生产代码通过 Modifier.semantics { … } 安装的 SemanticsPropertyKey。
  • 动作后的状态断言在 rule.runOnIdle { … } 内读取状态。
  • ./gradlew :app:connectedDebugAndroidTest(或用于 Robolectric 的 :app:testDebugUnitTest)对变更的测试通过。

参考

  • Compose 测试概览:https://developer.android.com/develop/ui/compose/testing
  • Compose 测试速查表:https://developer.android.com/develop/ui/compose/testing-cheatsheet
  • Compose 中的语义:https://developer.android.com/develop/ui/compose/accessibility/semantics
  • compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt — performClick (Actions.kt:58)、performScrollTo (Actions.kt:78)、performScrollToIndex (Actions.kt:159)、performScrollToKey (Actions.kt:188)、performScrollToNode (Actions.kt:233)、requestFocus (Actions.kt:600)、performSemanticsAction (Actions.kt:631 / Actions.kt:668)、performFirstLinkClick (Actions.kt:777)。
  • compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt — hasScrollAction、hasScrollToIndexAction、hasScrollToKeyAction、hasScrollToNodeAction。
  • compose/foundation/foundation/integration-tests/lazy-tests/.../LazyListTest.kt — 规范的 performScrollToIndex / performScrollToKey 模式。
  • compose/foundation/foundation/src/androidDeviceTest/.../CombinedClickableTest.kt — performClick、performTouchInput { longClick() }。
  • skydoves — compose-performance-skills:https://github.com/skydoves/compose-performance-skills