Blog Infos
Author
Published

 

This article will explore how the Accessibility Testing Framework (ATF) can automate accessibility-related checks for Compose-based UIs.

Starting with Compose version 1.8.0 We got support for Compose to perform accessibility checks in our UI tests automatically.

Enabling accessibility checks is currently only supported for AndroidComposeTestRule.

Required dependencies
  • Add the following dependencies to the version catalog libs.versions.toml file and then add it to your module’s build.gradle file or directly add to the module’s build.gradle

 

[versions]

composeBom = "2025.05.01"
uiTestJunit4Accessibility = "1.9.0-alpha03"
uiTestManifest = "1.9.0-alpha03"

[libraries]

androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4-accessibility", version.ref = "uiTestJunit4Accessibility" }
ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "uiTestManifest" }
App-level build file

 

androidTestImplementation(libs.androidx.ui.test.junit4.accessibility)

// Needed for createComposeRule()
debugImplementation(libs.ui.test.manifest)

 

Let’s write a UI test that includes accessibility checks ♿️
Composable

We have a UI with three components:

  • Text — Login sample screen
  • Text — Submit
  • Icon — 🧳

 

@Composable
fun LoginScreen(modifier: Modifier = Modifier) {
    Column(
        modifier = modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Login sample screen")
        
        SubmitCTA()
        // ....
    }

}

@Composable
fun SubmitCTA(modifier: Modifier = Modifier) {
    Row(
        horizontalArrangement = Arrangement.Center
    ) {
        Text(
            "Submit",
            // Set the color intentionally - color contrast issue
            style = MaterialTheme.typography.labelSmall.copy(
                color = Color.Gray
            )
        )
    }
}

 

Compose Preview

Enable accessibility checks on the Compose test rule

 

class ComposeUISampleTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Before
    fun setUp() {

        // Enable accessibility checks with default configuration:
        composeTestRule.enableAccessibilityChecks()

    }

// ....

 

  • After enabling the checks, they will automatically run when we perform actions.

 

class ComposeUISampleTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Before
    fun setUp() {

        // Enable accessibility checks with default configuration:
        composeTestRule.enableAccessibilityChecks()

    }

    @Test
    fun testSubmitAction() {
        composeTestRule.setContent {
            LoginScreen()
        }
        // Accessibility checks run automatically when performing an action:
        composeTestRule.onNodeWithText("Submit").performClick()
    }

 

  • When we run the test, it fails with the following logs ⧰😨

🛑 Text contrast ratio check fails for Submit text

com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityViewCheckException: There was 1 accessibility result:

View with bounds: [161,190][258,232]: The item's text contrast ratio is 3.40.
 
This ratio is based on an estimated foreground color of #888888 and
an estimated background color of #FAFAFA.

Consider using colors that result in a contrast ratio greater than 4.50 for small text,
or 3.00 for large text.
Let’s test another case — Content description
Composable

 

@Composable
fun LoginScreen(modifier: Modifier = Modifier) {
    Column {
        // ...  
      
        // Missing content description
        Icon(
            Icons.Outlined.Work,
            contentDescription = ""
        )
    }

}

 

Compose Preview

  • Test code stays the same in this case; When we rerun the test, we see the following logs

🛑 Missing content description for the work icon 🧳

 

com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityViewCheckException: 

There was 1 accessibility result:
View with bounds: [178,274][241,337]: 

This item may not have a label readable by screen readers. 

Reported by com.google.android.apps.common.testing.accessibility.framework.checks.SpeakableTextPresentCheck

 

Enable AccessibilityChecks with a custom validator
  • By default, when we enable the checks, it uses the default validator

 

public fun ComposeTestRule.enableAccessibilityChecks(
    accessibilityValidator: AccessibilityValidator =
        AccessibilityValidator().setRunChecksFromRootView(true)
) {

// .....

 

  • As you can see in the above code snippet, enableAccessibilityChecks take an AccessibilityValidator as a param. So if we want to customize the behavior, we can pass our custom validator.
  • The default implementation of AccessibilityValidator throws an exception for any Error.
  • Let’s say we want to throw an exception for 🛑 Error and ⚠️ Warning. In this case, we need to provide a custom validator.
Custom AccessibilityValidator implementation

 

val accessibilityValidator = AccessibilityValidator().apply {
    setThrowExceptionFor(AccessibilityCheckResult.AccessibilityCheckResultType.WARNING)
}

 

And then use this validator with enableAccessibilityChecks

class ComposeUISampleTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Before
    fun setUp() {

        // Enable accessibility checks with custom validator
        composeTestRule.enableAccessibilityChecks(
                           accessibilityValidator = accessibilityValidator)
   
 // ...
  • Now, if there is any accessibility related warning or error, our test will throw an exception.

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.

With the advent of Android 15, edge-to-edge design has become the default configuration. Consequently, applications must be capable of accommodating window insets, including the system status bar and navigation bar, as well as supporting drawing…
Watch Video

Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.

Timo Drick
Lead Android developer
Seven Principles Mobility GmbH

Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.

Timo Drick
Lead Android develop ...
Seven Principles Mob ...

Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.

Timo Drick
Lead Android developer
Seven Principles Mobility ...

Jobs

Perform accessibility checks manually
  • We can use tryPerformAccessibilityChecks() to perform accessibility checks manually

 

class ComposeUISampleTest {

    @Test
    fun testSubmitCTAAction() {
        
        // You can also manually run accessibility checks:
        composeTestRule.onRoot().tryPerformAccessibilityChecks()

      
    }

 

Disable accessibility checks
  • We can use disableAccessibilityChecks() to disable the accessibility checks

 

class ComposeUISampleTest {

    @Test
    fun disableChecksSample() {

        // disabling accessibility checks..
        composeTestRule.disableAccessibilityChecks()
    }

 

Let’s make Android applications more accessible
Stay in touch

https://www.linkedin.com/in/navczydev/

https://x.com/navczydev

References

This article was previously published on proandroiddev.com.

Menu