Enabling Accessibility Checks — 在 Compose 语义树上启用 Espresso 的 AccessibilityValidator
用途
Compose 无障碍测试构件把 Espresso 的 AccessibilityValidator(来自 com.google.android.apps.common.testing.accessibility.framework.integrations.espresso)接入 Compose 操作管线。一旦启用,每个会修改界面的操作(performClick、performScrollTo*、performTextInput 等)都会先针对底层 View 运行校验器。该技能用于给纯 Compose 屏幕添加无障碍回归覆盖,例如当审查者要求一个在对比度、触摸目标或标签规则回归时能让构建失败的无障碍测试,或提到 "AccessibilityChecks.enable"、"AccessibilityValidator"、"Compose accessibility validator" 时。两个构件分别覆盖两个测试入口(ComposeUiTest 与 ComposeTestRule),一个是稳定版,一个是实验版。需要真实设备 API 34+ 才有可信结果——在 Robolectric 上校验器仍会安装,但会输出 Log.w 警告,任何通过都不确定(Robolectric 无法忠实驱动无障碍服务)。
主要能力
- 通过 enableAccessibilityChecks(...) 在 Compose 语义树上启用 Espresso 的 AccessibilityValidator。
- 默认校验器为 AccessibilityValidator().setRunChecksFromRootView(true)。
- 可通过自定义 AccessibilityValidator 调整阈值,例如从仅 WARNING 改为 ERROR:
val validator = AccessibilityValidator().apply {
setThrowExceptionFor(AccessibilityCheckResultType.ERROR)
}
rule.enableAccessibilityChecks(validator)
- 自动校验会在每一个修改界面的操作前触发。tryPerformAccessibilityChecks() 在 performClick、performScrollTo、performScrollToIndex/Key/Node、performTouchInput、performMouseInput、performKeyInput、performTrackpadInput、performRotaryScrollInput、performIndirectPointerInput、performFirstLinkClick 以及文本操作族开头被调用。performMultiModalInput 不会自动校验,需开发者显式调用 tryPerformAccessibilityChecks()。requestFocus() 走 performSemanticsAction,同样不会自动校验。
- 可手动运行校验(用于 performMultiModalInput,或把关某个阶段):
rule.onRoot().tryPerformAccessibilityChecks()
- 可与 Espresso 共享校验器,用于 Compose + View 混合屏幕。AccessibilityChecks.enable() 返回 Espresso 使用的同一校验器实例,传给 Compose 后两层按同一配置报告:
import androidx.test.espresso.accessibility.AccessibilityChecks
@OptIn(ExperimentalTestApi::class)
@Test
fun hybrid() = runAndroidComposeUiTest<ComponentActivity> {
val validator = AccessibilityChecks.enable()
enableAccessibilityChecks(validator)
}
- 退出隔离阶段时可禁用校验:
rule.disableAccessibilityChecks()
使用方式
前置条件:
- 运行 API 34+(Android U)的真实 Android 设备或模拟器。
- 以下二选一:
- androidx.compose.ui:ui-test-accessibility 加入 androidTestImplementation——扩展 ComposeUiTest。@RequiresApi(34) @ExperimentalTestApi,需要 @OptIn(ExperimentalTestApi::class)。
- androidx.compose.ui:ui-test-junit4-accessibility 加入 androidTestImplementation——扩展 AndroidComposeTestRule 与 ComposeTestRule。仅 @RequiresApi(34),非实验。
- 具备来自 structuring-a-compose-test 的测试类骨架。
工作流:
- 每个测试启用一次校验(或在 @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)
}
默认校验器签名:
@RequiresApi(34)
public fun <R : TestRule, A : ComponentActivity> AndroidComposeTestRule<R, A>.enableAccessibilityChecks(
accessibilityValidator: AccessibilityValidator =
AccessibilityValidator().setRunChecksFromRootView(true),
)
对 ComposeUiTest(例如在 runComposeUiTest { } 内),等效方法位于 androidx.compose.ui.test.accessibility,需要 @OptIn(ExperimentalTestApi::class):
@OptIn(ExperimentalTestApi::class)
@Test
fun runs() = runAndroidComposeUiTest<ComponentActivity> {
enableAccessibilityChecks()
onNodeWithText("Submit").performClick()
}
-
通过 AccessibilityValidator 配置更严格的模式(见上文示例)。
-
自动校验会在每个修改界面的操作前触发。
-
需要时手动运行校验(见上文示例)。
-
与 Espresso 共享校验器用于混合屏幕(见上文示例)。
-
离开隔离阶段时禁用校验(见上文示例)。
-
仅当规则确实是 AndroidComposeTestRule 时才使用 ComposeTestRule 重载。非 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
适用场景与限制
适用:为纯 Compose 屏幕添加无障碍回归覆盖;审查者要求对比度/触摸目标/标签规则回归时构建失败的 a11y 测试;混合 Compose + Views 屏幕需要 Espresso 与 Compose 共享单一 AccessibilityValidator;a11y 检查在 Robolectric 上似乎无效果时。
不适用:检查失败源于错误的 finder 而非 a11y 违规;测试在 Robolectric(宿主 JVM)上运行;测试目标 API 低于 34(@RequiresApi(34) 会使调用在较低 minSdk 下无法编译,需在测试方法上用 @SdkSuppress(minSdkVersion = 34) 并在旧设备上跳过);用户想要的是语义测试覆盖(角色、内容描述、点击动作)。
强制规则与限制:
- 任何调用 enableAccessibilityChecks 的测试必须以真实设备 API 34+ 为目标。不得依赖 Robolectric 上的检查产生有意义结果——Build.FINGERPRINT.lowercase() == "robolectric" 会触发 Log.w("...", "Accessibility checks are currently not supported by Robolectric"),校验器仍会安装但无法依赖 Robolectric 未忠实模拟的无障碍服务。Robolectric 下的任何通过都不确定。
- 使用规则入口点时优先使用稳定的 androidx.compose.ui:ui-test-junit4-accessibility;对 ComposeUiTest 使用 androidx.compose.ui:ui-test-accessibility 时须加 @OptIn(ExperimentalTestApi::class)。
- 在 performMultiModalInput { … } 之后以及 requestFocus() 之后,若需要对这些操作校验,须手动调用 tryPerformAccessibilityChecks()。
- 不得对非 AndroidComposeTestRule 的 ComposeTestRule 调用 enableAccessibilityChecks,非 Android 重载会抛出 NotImplementedError。
- 推荐通过 AccessibilityValidator().setThrowExceptionFor(...) 将阈值提升到 AccessibilityCheckResultType.ERROR,使违规让测试失败而非仅记录日志。
- 在混合 Compose + Views 套件中,推荐通过 AccessibilityChecks.enable() 共享一个 AccessibilityValidator 并同时传给 Espresso 与 Compose。
验证要点:测试运行于真实设备 API 34+;若模块 minSdk 更低需有 @SdkSuppress(minSdkVersion = 34);测试输出中无 Robolectric Log.w 警告;相关构件已加入 androidTestImplementation;enableAccessibilityChecks(...) 每个测试(或 @Before)调用一次;disableAccessibilityChecks() 仅在有意抑制校验时调用;任何 performMultiModalInput { … } 后跟随显式 rule.onRoot().tryPerformAccessibilityChecks();混合套件通过 AccessibilityChecks.enable() 获取校验器并传给 enableAccessibilityChecks(validator);不在非 AndroidComposeTestRule 实例上调用 enableAccessibilityChecks。
参考:Espresso accessibility checks(https://developer.android.com/training/testing/espresso/accessibility-checking)、Test for accessibility (Compose)(https://developer.android.com/develop/ui/compose/accessibility/testing)、Compose UI testing release notes(https://developer.android.com/jetpack/androidx/releases/compose-ui)。