Appium Example

Link to Appium Example copied to clipboard

We're developing an Appium Plugin that's set to launch later this year. This plugin will enable you to better integrate accessibility testing throughout your Appium tests, no matter the language they are written in!

Using Appium with axe DevTools Today? The below information is provided in context of implementing the axeDevToolsUIKit framework (setup available to view here) and engaging with an embedded floating action button to kick off a scan. The axeDevToolsUIKit framework is decommissioned and will not recieve new updates while the plugin is being built.

Supported within:
axeDevTools UIKit framework


Before running your tests in Appium, ensure the application under test is set up to run axe DevTools Mobile by embedding the framework into your application. Resource: Setup Guide for Embedding the framework

Next, replace the call to axe?.showA11yFAB() with axe?.showA11yFAB(customFAB: AutomatedTestFAB()). This provides an invisible floating action button for tests to engage with that won't interfere with any UI assertions. Resource: UITesting with axeDevToolsUIKit


By embedding the framework into your application, Appium can initiate a scan by engaging with the floating action button by its accessibility identifier: com.deque.axeDevTools.accessibilityFab. Engage with the floating action button at any point in your tests you'd like to scan for accessibility issues.

Note: Appium support for accessibility testing is limited to UIKit implementations currently. Support for other languages is coming soon.


Python Client

Resource: Appium Python Client Documentation

Follow the comments marked with TODO to update your specific configurations to start testing with the script as is. Grab a Deque API Key from your axe Account Settings page.

from appium import webdriver
import os

import requests
from appium.webdriver import WebElement
from appium.webdriver.common.appiumby import AppiumBy

## TODO: Update your configurations
app = "com.dequesystems.axe-devtools-ios-sample-app"
udid = "DFB0EB3F-2C3D-4F62-B684-39F3D12F0DCD"
platformVersion = "16.2"
deviceName = "iPhone 14"
dequeAPIKey = ""

## Class Definitions for parsing the result from the server:
class AxeRuleResult:
    def __init__(self, ruleId, ruleSummary, axeViewId, status, impact, props, isVisibleToUser=True):
        self.ruleId = ruleId
        self.ruleSummary = ruleSummary
        self.axeViewId = axeViewId
        self.status = status
        self.impact = impact
        self.props = props
        self.isVisibleToUser = isVisibleToUser
class AxeResult:
    def __init__(self, axeRuleResults, userName, scanName, tags):
        self.axeRuleResults = axeRuleResults
        self.userName = userName
        self.scanName = scanName
        self.tags = tags

class DemoWithAPIRequest:
    def __init__(self):
        self.driver = None
        self.client = requests.Session()

    def setup(self):
        success = True
        desired_caps = {}
        desired_caps['platformName'] = 'iOS'
        desired_caps['platformVersion'] = platformVersion
        desired_caps['deviceName'] = deviceName
        desired_caps['udid'] = udid
        desired_caps['automationName'] = 'XCUITest'
        desired_caps['realDeviceLogger'] = 'idevicesyslog'
        desired_caps['app'] = app
        ## TODO: UPDATE URL for your appium server:
        self.driver = webdriver.Remote('', desired_caps)

    def runA11y(self):
        # Navigate and Click the FAB
        fab = self.driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='com.deque.axeDevTools.accessibilityFab')

        # Wait for network request to finish and FAB to return.

        # Getting Result Key from the FAB
        resultKey = fab.get_attribute("label")

        # Fetch Rule Results from API
        url = ""+resultKey
        headers = {
            "X-Api-Key": dequeAPIKey,
            "Content-Type": "application/json"
            response = self.client.get(url, headers=headers)
            if response.status_code == 200:
                content = response.json()

                # Parse JSON and output the result
                axeResult = AxeResult(
                    axeRuleResults=[AxeRuleResult(**ruleResult) for ruleResult in content['axeRuleResults']],

                for rule in axeResult.axeRuleResults:
                    if rule.status == "FAIL":
                        print(f"rule: {rule.ruleId}")
                print(f"httpCode: {response.status_code}")
        except Exception as e:
            print(f"error: {str(e)}")

demo = DemoWithAPIRequest()

Java Client

Resource: Appium Java Client Documentation


plugins {
    id 'java'
    id 'org.jetbrains.kotlin.jvm' version '1.5.0'

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    maven { url '' }
    maven { url "" }

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib"
    implementation "junit:junit:4.13.2"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2"
    testImplementation "junit:junit:4.13.1"

    implementation "com.github.appium:java-client:8.1.1"
    implementation "org.seleniumhq.selenium:selenium-java:4.2.1"
    implementation ""
    implementation "com.squareup.okhttp3:okhttp:4.10.0"

Full Example

import io.appium.java_client.AppiumBy
import io.appium.java_client.ios.IOSDriver
import io.appium.java_client.remote.MobileCapabilityType
import io.appium.java_client.remote.MobilePlatform
import okhttp3.OkHttpClient
import okhttp3.Request
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.openqa.selenium.WebDriver
import org.openqa.selenium.remote.DesiredCapabilities
import java.time.Duration

class DemoWithAPIRequest {
    private val client = OkHttpClient()
    private lateinit var driver: IOSDriver
    val appFilePath = "path/to/your/"

    fun setup() {
        val capabilities = DesiredCapabilities()

        capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone 14")
        capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.IOS)
        capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "16.0")
        capabilities.setCapability(MobileCapabilityType.APP, File(appFilePath).absolutePath)

        driver = IOSDriver(URL(""), capabilities)

    fun runA11y() {
        // Navigate and Click the FAB

        val webDriverWait = WebDriverWait(driver, Duration.ofSeconds(10))
        webDriverWait.until(ExpectedCondition { input: WebDriver? ->

        // Getting Result Key from the FAB
        val resultKey: String = driver.findElement(

        // Fetch Rule Results from API
        val apiKey = "your-api-key-here"
        val url = "$resultKey"
        val request = Request.Builder()
            .addHeader("X-Api-Key", apiKey)
            .addHeader("Content-Type", "application/json")

        try {
            val response = client.newCall(request).execute()
            if (response.code == 200) {
                val content = response.body?.string()
                // Parse JSON and output the result
                val axeResult = Gson().fromJson(content,

                axeResult.axeRuleResults.forEach {
                    if (it.status == "FAIL") {
                        println("rule: ${it.ruleId}")
            } else {
                println("httpCode: ${response.code}")
        } catch (t: Throwable) {
            println("error: ${t.message}")

data class AxeRuleResult(
    val ruleId: String?,
    val ruleSummary: String?,
    val axeViewId: String?,
    val status: String?, // "PASS", "FAIL", "INCOMPLETE"
    val impact: Int?,
    val props: HashMap<String, Any?>,
    val isVisibileToUser: Boolean = true

data class AxeResult(
    val axeRuleResults: List<AxeRuleResult>,
    val userName: String?,
    val scanName: String?,
    val tags: List<String>?