Reference for Compose Layout Testing

Link to Reference for Compose Layout Testing copied to clipboard
warning

Important Update: Android API Transition

If you are just getting started or looking to simplify your axe DevTools for Mobile implementation, we recommend starting with our layout agnostic APIs.

Utilizing the Compose APIs today?

After great consideration, we are moving toward a streamlined implementation of axe DevTools for Android that will result in the XML and Compose APIs being deprecated at a future date. While this transition will not happen over night, we are encouraging customers to gradually shift to the layout agnostic APIs for continued support and access to the latest updates.

This page highlights the steps for setting up the library to scan Compose layouts for accessibility issues. For steps to pull in the library, please see Getting Started.

Automated Testing

Initialize the Library

In the test class init, connect to the library with one of the following:

Connect with API key:

Generate an API key at axe.deque.com.

private val axe = AxeDevToolsCompose()

init {
     axe.loginWithApiKey(
        "DEQUE_API_KEY"
    )
}

Connect with username and password:

private val axe = AxeDevToolsCompose()

init {
    axe.loginWithUsername(
        "DEQUE_USERNAME",
        "DEQUE_PASSWORD"
    )
}

Run a Scan

When you're ready to run a scan from your tests, you'll want to run axe.scan(). We've included a helper function below that highlights some additional functionality you may find helpful.

Read about these features and more on the Android Features page. This function is broken down below the snippet.


private fun a11yScan() {
    /*
    Note: This test relies on the compose test rule from `setup` method. Please see the full example at the bottom of this guide.
    */

    val scanResultHandler = axe.scan()

    //1. Upload it to the dashboard
    scanResultHandler?.uploadToDashboard()

    //2. Using the results in your test suite
    val result: AxeResult? = scanResultHandler?.getSerializedResult()
    result?.axeRuleResults?.forEach { result ->
        if(result.status == AxeStatus.PASS) {

        }
        else if(result.status == AxeStatus.FAIL) {

        }
        else if(result.status == AxeStatus.INCOMPLETE) {

        }
    }

    //3. Save the result JSON to a local file for later use
    scanResultHandler?.saveResultToLocalStorage("your_file_prefix")
}

Code snippet breakdown:

  1. From the result handler, call uploadToDashboard() to send the most recent scan to the dashboard. The scan will be published from the authenticated account used in the init function.
  2. From the result handler, call getSerializedResult() to use results from the library locally. For example, you may choose to fail the test when failures are found in the accessibility scan results. This will not upload your results to the server. Uploading your results is not required to get the results locally.
  3. From the result handler, call saveResultToLocalStorage() to save the results as JSON in a local file within your test device. To access the saved file, refer to the feature documentation for Saving Results Locally.

Deinitialize

Once the test has been completed, call tearDown() on the AxeDevToolsCompose object you have created to clear the state of previous tests. We recommend putting this in your testing file's tearDown function, but it can also be called within a test if needed.

@After
fun tearDown() {
    axe.tearDown()
}

Example Espresso Test Class for Compose


@ExperimentalComposeUiApi
class ExampleComposeTest {
    @get:Rule
    val composeTestRule = createAndroidComposeRule<TestComposeActivity>()

    private val axe = AxeDevToolsCompose()

    init {
        // Connect using an API key 
        axe.loginWithApiKey(
            "DEQUE_API_KEY"
        )
    }

    @Before
    fun setup() {
        composeTestRule.setContent { YourComposableFunction() }
        composeTestRule.mainClock.autoAdvance = false
        composeTestRule.waitForIdle()

        axe.tagScanAs(setOf("ScanningAnApp"))
    }

    @Test
    fun foobar() {
        // Must be set in the tests itself
        axe.setComposeTestRule(composeTestRule)
        val scan = axe.scan()
        scan?.uploadToDashboard()
        axe.tearDown()
    }
}