关于 enabling-accessibility-checks
此技能指南介绍如何在 Jetpack Compose 测试中启用 Espresso AccessibilityValidator,覆盖 UI 操作并检查无障碍问题。适用于编写 Android 无障碍回归测试的开发者,也说明 API 34+ 设备要求及 Robolectric 的局限。
主要功能
- 01为 Compose 测试启用无障碍检查
- 02配置警告或错误阈值
- 03覆盖 UI 变更操作
- 04手动触发检查
- 05与 Espresso 共用验证器
适用场景
- 01为 Compose 屏幕添加无障碍回归测试
- 02让无障碍违规导致测试失败
- 03检查 Robolectric 测试结果为何无效
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ skydoves/ android-testing-skills --skill enabling-accessibility-checks安装后在 Claude Code 中输入/enabling-accessibility-checks确认已加载。
详细介绍
启用无障碍检查——在 Compose 树上使用 Espresso 的 AccessibilityValidator
Compose 无障碍测试制品会将 Espresso 的 AccessibilityValidator(来自 com.google.android.apps.common.testing.accessibility.framework.integrations.espresso)接入 Compose 操作流水线。启用后,每个会修改 UI 的操作(performClick、performScrollTo*、performTextInput 等)都会先针对底层 View 运行验证器。有两种不同的制品分别覆盖两个测试入口(ComposeUiTest 和 ComposeTestRule),其中一个稳定,另一个为实验性。若要获得有意义的结果,需使用真实设备和 API 34+;在 Robolectric 上,验证器仍会安装,但会发出 Log.w 警告,任何通过结果都不能作为定论(Robolectric 无法忠实地驱动无障碍服务)。
何时使用此技能
- 开发者正在为仅使用 Compose 的屏幕添加无障碍回归测试。
- 审查者要求添加无障碍测试,以便在对比度/触控目标/标签规则回退时使构建失败。
- 开发者提到“AccessibilityChecks.enable”“AccessibilityValidator”或“Compose accessibility validator”。
- 混合使用 Compose 和 Views 的屏幕需要在 Espresso 与 Compose 之间共享同一个
AccessibilityValidator。 - 开发者的无障碍检查似乎在 Robolectric 上不起作用(
Log.w警告说明了原因)。
何时不要使用此技能
- 检查失败是因为查找器有误,而非无障碍违规。请使用
../printing-the-semantics-tree/SKILL.md。 - 测试运行在 Robolectric(宿主 JVM)上。此限制见
compose/ui/ui-test-accessibility/src/androidMain/kotlin/androidx/compose/ui/test/accessibility/ComposeUiTestExt.android.kt:50-53。请将测试移至androidDeviceTest,并使用 API 34+ 的真实设备——参见../../setup/setting-up-host-vs-device-tests/SKILL.md。 - 测试目标 API 低于 34。注解
@RequiresApi(34)会导致调用在较低的 minSdk 上无法编译。请在测试方法上使用@SdkSuppress(minSdkVersion = 34),并在较旧设备上跳过测试。 - 用户想要的是语义测试覆盖(角色、内容描述、点击操作)。请使用
../../assertions/asserting-node-state-and-text/SKILL.md。
前置条件
- 运行 API 34+(Android U)的真实 Android 设备(或模拟器)。
- 以下两者之一:
- 在
androidTestImplementation中添加androidx.compose.ui:ui-test-accessibility——扩展ComposeUiTest。仅支持@RequiresApi(34) @ExperimentalTestApi。需要@OptIn(ExperimentalTestApi::class)。 - 在
androidTestImplementation中添加androidx.compose.ui:ui-test-junit4-accessibility——扩展AndroidComposeTestRule和ComposeTestRule。仅需@RequiresApi(34)——非实验性。
- 在
- 来自
../../patterns/structuring-a-compose-test/SKILL.md的测试类骨架。
工作流
- 1. 每次测试启用一次检查(或在
@Before中启用)。AndroidComposeTestRule的 API:
import androidx.compose.ui.test.junit4.accessibility.enableAccessibilityChecks
import com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityValidator
@Before
fun before() {
rule.enableAccessibilityChecks() // default: AccessibilityValidator().setRunChecksFromRootView(true)
}
默认验证器的签名,引用自 compose/ui/ui-test-junit4-accessibility/src/androidMain/kotlin/androidx/compose/ui/test/junit4/accessibility/AndroidComposeTestRuleExt.android.kt:44-49:
@RequiresApi(34)
public fun <R : TestRule, A : ComponentActivity> AndroidComposeTestRule<R, A>.enableAccessibilityChecks(
accessibilityValidator: AccessibilityValidator =
AccessibilityValidator().setRunChecksFromRootView(true),
)
对于 ComposeUiTest(例如在 runComposeUiTest { } 内),对应 API 位于 androidx.compose.ui.test.accessibility,并且需要 @OptIn(ExperimentalTestApi::class):
@OptIn(ExperimentalTestApi::class)
@Test
fun runs() = runAndroidComposeUiTest<ComponentActivity> {
enableAccessibilityChecks()
onNodeWithText("Submit").performClick()
}
引用自 compose/ui/ui-test-accessibility/samples/src/main/java/androidx/compose/ui/test/accessibility/samples/AccessibilityChecksSamples.kt:42-61。
- 2. 通过
AccessibilityValidator本身配置更严格的模式。 传入自定义验证器,将阈值从仅 WARNING 切换为 ERROR。
val validator = AccessibilityValidator().apply {
setThrowExceptionFor(AccessibilityCheckResultType.ERROR)
}
rule.enableAccessibilityChecks(validator)
引用自 AccessibilityChecksSamples.kt:68-82。
-
3. 每次修改 UI 的操作之前都会触发自动检查。 引用自
compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt:79, 160, 189, 236, 402, 451, 496, 535, 697, 780, 948—tryPerformAccessibilityChecks()会在performClick、performScrollTo、performScrollToIndex/Key/Node、performTouchInput、performMouseInput、performKeyInput、performTrackpadInput、performRotaryScrollInput、performIndirectPointerInput、performFirstLinkClick以及文本操作系列的开头调用。performMultiModalInput不会自动检查(Actions.kt:582);开发者必须显式调用tryPerformAccessibilityChecks()。requestFocus()(Actions.kt:600-601)会通过performSemanticsAction执行,同样不会自动检查 — 如果需要覆盖聚焦后的无障碍检查,请在聚焦后调用tryPerformAccessibilityChecks()。 -
4. 需要时手动运行检查(用于
performMultiModalInput,或用于设置阶段门控)。
rule.onRoot().tryPerformAccessibilityChecks()
引用自 AccessibilityChecksSamples.kt:53。
- 5. 在 Compose + View 混合屏幕中与 Espresso 共享验证器。
AccessibilityChecks.enable()会返回 Espresso 使用的同一验证器实例;将其传给 Compose,以便两个层使用同一配置报告结果。
import androidx.test.espresso.accessibility.AccessibilityChecks
@OptIn(ExperimentalTestApi::class)
@Test
fun hybrid() = runAndroidComposeUiTest<ComponentActivity> {
val validator = AccessibilityChecks.enable()
enableAccessibilityChecks(validator)
}
引用自 AccessibilityChecksSamples.kt:88-98。
- 6. 离开独立阶段时禁用检查。
rule.disableAccessibilityChecks()
引用自 AndroidComposeTestRuleExt.android.kt:71-75。
- 7. 仅当规则实际为
AndroidComposeTestRule时,才使用ComposeTestRule重载。 引用自compose/ui/ui-test-junit4-accessibility/src/androidMain/kotlin/androidx/compose/ui/test/junit4/accessibility/ComposeTestRuleExt.android.kt:38-49。非 Android 规则会抛出NotImplementedError:
Enabling accessibility checks is currently only supported for AndroidComposeTestRule.
If you have a custom ComposeTestRule implementation that wraps an AndroidComposeTestRule,
directly call enableAccessibilityChecks on the AndroidComposeTestRule instead
模式
模式:在 Robolectric 上启用 — 结果不确定
// WRONG (assertion is meaningless under Robolectric)
@Config(sdk = [34])
@RunWith(RobolectricTestRunner::class)
class MyA11yTest {
@get:Rule val rule = createComposeRule(StandardTestDispatcher())
@Test fun checks() {
rule.enableAccessibilityChecks() // logs Log.w; validator IS installed
rule.setContent { /* contrast violation */ }
rule.onNodeWithTag("submit").performClick() // may PASS, may FAIL — inconclusive
}
}
// WRONG because: HasRobolectricFingerprint = (Build.FINGERPRINT.lowercase() == "robolectric")
// triggers a Log.w but the validator is STILL installed (ComposeUiTestExt.android.kt:50-61
// AND AndroidComposeTestRuleExt.android.kt:50-64). Robolectric does not faithfully drive
// the accessibility services, so any result is inconclusive. Tracking bug: b/332778271.
// Run accessibility checks ONLY on a real device API 34+ for trustworthy results.
// RIGHT — move the test to androidDeviceTest and run on a real device API 34+
@MediumTest
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = 34)
class MyA11yTest {
@get:Rule val rule = createComposeRule(StandardTestDispatcher())
@Before fun before() { rule.enableAccessibilityChecks() }
@Test fun submitPassesA11y() {
rule.setContent { MyScreen() }
rule.onNodeWithTag("submit").performClick()
}
}
模式:performMultiModalInput 会跳过自动检查
// WRONG
@Test
fun multiModal() {
rule.enableAccessibilityChecks()
rule.setContent { MyScreen() }
rule.onNodeWithTag("canvas").performMultiModalInput {
touch { down(center); up() }
key { pressKey(Key.Enter) }
}
// No a11y check ran for this action — Actions.kt:582 omits tryPerformAccessibilityChecks().
}
// RIGHT
@Test
fun multiModal() {
rule.enableAccessibilityChecks()
rule.setContent { MyScreen() }
rule.onNodeWithTag("canvas").performMultiModalInput { … }
rule.onRoot().tryPerformAccessibilityChecks() // explicit
}
模式:ComposeTestRule(非 Android)重载会抛出异常
// WRONG
val rule: ComposeTestRule = MyCustomRuleThatWrapsAndroid()
rule.enableAccessibilityChecks() // throws NotImplementedError
// WRONG because: only AndroidComposeTestRule is supported. Cited at
// ComposeTestRuleExt.android.kt:38-49.
// RIGHT — call on the underlying AndroidComposeTestRule
val rule = AndroidComposeTestRule(/* … */)
rule.enableAccessibilityChecks()
模式:与 Espresso 共享验证器
// RIGHT
@OptIn(ExperimentalTestApi::class)
@Test
fun shared() = runAndroidComposeUiTest<ComponentActivity> {
val validator = AccessibilityChecks.enable().apply {
setThrowExceptionFor(AccessibilityCheckResultType.ERROR)
}
enableAccessibilityChecks(validator)
onNodeWithText("Submit").performClick()
}
强制规则
- 必须使用真实设备 API 34+,运行任何调用
enableAccessibilityChecks的测试。不得依赖 Robolectric 环境下的检查结果有实际意义——Build.FINGERPRINT.lowercase() == "robolectric"会触发Log.w("...", "Accessibility checks are currently not supported by Robolectric")(ComposeUiTestExt.android.kt:50-53、AndroidComposeTestRuleExt.android.kt:50-56);验证器仍会安装,但不能依赖 Robolectric 无法忠实模拟的无障碍服务。将 Robolectric 下的任何通过结果视为无法确定。跟踪问题:b/332778271。 - 必须在使用规则入口点时,优先选择稳定版
androidx.compose.ui:ui-test-junit4-accessibility;在ComposeUiTest上使用androidx.compose.ui:ui-test-accessibility时,必须应用@OptIn(ExperimentalTestApi::class)。 - 必须在
performMultiModalInput { … }之后以及requestFocus()之后手动调用tryPerformAccessibilityChecks(),以便测试验证这些操作——Actions.kt:582中,performMultiModalInput未触发自动检查;而requestFocus()(Actions.kt:600-601)通过performSemanticsAction执行,同样不会触发自动检查。 - 不得在并非
AndroidComposeTestRule的ComposeTestRule上调用enableAccessibilityChecks。非 Android 版本的重载会抛出NotImplementedError(ComposeTestRuleExt.android.kt:38-49)。 - **建议:**通过
AccessibilityValidator().setThrowExceptionFor(...)将阈值提高到AccessibilityCheckResultType.ERROR,让违规导致测试失败,而不仅仅是记录日志。 - **建议:**在 Compose 与 Views 混合的测试套件中,通过
AccessibilityChecks.enable()共享一个AccessibilityValidator,并将其同时传递给 Espresso 和 Compose。引用位置:AccessibilityChecksSamples.kt:88-98。
验证
- 测试在真实设备 API 34+ 上运行。如果模块的 minSdk 较低,则存在
@SdkSuppress(minSdkVersion = 34)。 - 测试输出中不会出现 Robolectric 日志警告 "Accessibility checks are currently not supported by Robolectric"。
- 相关依赖已添加到
androidTestImplementation:androidx.compose.ui:ui-test-junit4-accessibility(规则路径),或androidx.compose.ui:ui-test-accessibility+@OptIn(ExperimentalTestApi::class)(ComposeUiTest路径)。 - 每个测试中(或在
@Before中)调用一次enableAccessibilityChecks(...);仅在有意抑制检查时调用disableAccessibilityChecks()。 - 每处
performMultiModalInput { … }调用后,都显式调用rule.onRoot().tryPerformAccessibilityChecks()。 - Compose+View 混合测试套件通过
AccessibilityChecks.enable()获取验证器,并将其传递给enableAccessibilityChecks(validator)。 - 不会在非
AndroidComposeTestRule实例上调用enableAccessibilityChecks。
参考资料
enableAccessibilityChecks(规则路径):compose/ui/ui-test-junit4-accessibility/src/androidMain/kotlin/androidx/compose/ui/test/junit4/accessibility/AndroidComposeTestRuleExt.android.kt:44-75ComposeTestRule重载 — NotImplementedError:compose/ui/ui-test-junit4-accessibility/src/androidMain/kotlin/androidx/compose/ui/test/junit4/accessibility/ComposeTestRuleExt.android.kt:38-66enableAccessibilityChecks(ComposeUiTest 路径):compose/ui/ui-test-accessibility/src/androidMain/kotlin/androidx/compose/ui/test/accessibility/ComposeUiTestExt.android.kt:44-77- Robolectric 的结果不确定行为(发出警告并仍然安装验证器):
ComposeUiTestExt.android.kt:50-61、AndroidComposeTestRuleExt.android.kt:50-64、b/332778271 Actions.kt中自动检查的调用位置:compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt:79, 160, 189, 236, 402, 451, 496, 535, 582 (omitted), 697, 780, 948- 示例:
compose/ui/ui-test-accessibility/samples/src/main/java/androidx/compose/ui/test/accessibility/samples/AccessibilityChecksSamples.kt:42-98 - Espresso 无障碍检查:https://developer.android.com/training/testing/espresso/accessibility-checking
- 无障碍测试(Compose):https://developer.android.com/develop/ui/compose/accessibility/testing
- Compose UI 测试发行说明:https://developer.android.com/jetpack/androidx/releases/compose-ui