Appium with Java

Link to Appium with Java copied to clipboard
tip

Codeless accessibility testing via Appium is Coming Soon!

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

Using Appium with axe DevTools Today?

Today Appium support is only available for native Android XML applications via an embedded code element. Checkout the guide to get started with the embedded code element and come back here once you've hit Step 3.

The following Appium example is written in Java and assumes the use of Appium's java-client. The logic is transferable to other clients.

Full Example

import com.deque.axe.android.constants.AxeStatus;

import com.deque.networking.interfaces.AxeDevToolsClient;
import com.deque.networking.interfaces.ConnectionConfig;
import com.deque.networking.interfaces.ResultsDashboard;
import com.deque.networking.interfaces.data.TagsSet;
import com.deque.networking.models.auth.AccessToken;
import com.deque.networking.models.devtools.serializable.AxeDevToolsResult;
import com.deque.networking.models.devtools.serializable.AxeDevToolsResultKey;
import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.remote.AutomationName;
import io.appium.java_client.remote.MobileCapabilityType;
import io.appium.java_client.remote.MobilePlatform;

import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

public class AppiumTest {
    OkHttpClient client = new OkHttpClient();
    String apiKey = "your-api-key-here";
    String appPath = "path/to/app.apk";

    AndroidDriver driver;

    AndroidDriver makeDriver() throws MalformedURLException {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator");
        capabilities.setCapability(MobileCapabilityType.APP, new File(appPath).getAbsolutePath());
        capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.ANDROID);
        capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2);

        String DEFAULT_APPIUM_ADDRESS = "http://0.0.0.0:4723/";
        return new AndroidDriver(new URL(DEFAULT_APPIUM_ADDRESS), capabilities);
    }

    @Before
    public void setup() throws MalformedURLException {
        driver = makeDriver();
    }

    @Test
    public void test() {
        driver.findElement(
                new AppiumBy.ByAccessibilityId("Axe")
        ).click();

        WebDriverWait webDriverWait = new WebDriverWait(driver, Duration.ofSeconds(10));
        webDriverWait.until((ExpectedCondition<Boolean>) input -> {
            if (input != null) {
                input.findElement(AppiumBy.id("result_key_container"));
            }
            return true;
        });

        String resultKey = driver.findElement(
                AppiumBy.id("result_key_container")
        ).getAttribute("content-desc");

        Request request = new Request.Builder()
                .url("https://axe-mobile-backend.deque.com/attest/result/axe/" + resultKey)
                .addHeader("X-Api-Key", apiKey)
                .addHeader("Content-Type", "application/json")
                .build();

        try {
            Response response = client.newCall(request).execute();
            if (response.code() == 200) {
                String content = response.body().string();
                AxeResult1 axeResult1 = new Gson().fromJson(content, AxeResult1.class);

                axeResult1.ruleResults.forEach(it -> {
                    if (it.status.equals("FAIL")) {
                        System.out.println("FAILED: " + it.ruleId);
                    }
                });

            }
        } catch (Throwable throwable) {
            System.out.println(throwable.getLocalizedMessage());
        }
    }
}

class AxeRuleResult1 {
    String ruleId;
    String ruleSummary;
    String axeViewId;
    String status;
    Integer impact;
    HashMap<String, Object> props;
    Boolean isVisibleToUser;
    AxeRuleResult1(
            String ruleId,
            String ruleSummary,
            String axeViewId,
            String status,
            Integer impact,
            HashMap<String, Object> props,
            Boolean isVisibleToUser
    ) {
        this.ruleId = ruleId;
        this.ruleSummary = ruleSummary;
        this.axeViewId = axeViewId;
        this.status = status;
        this.impact = impact;
        this.props = props;
        this.isVisibleToUser = isVisibleToUser;
    }
}

class AxeResult1 {
    List<AxeRuleResult1> ruleResults;
    String userName;
    String scanName;
    List<String> tags;
    AxeResult1(
            List<AxeRuleResult1> ruleResults,
            String userName,
            String scanName,
            List<String> tags
    ) {
        this.ruleResults = ruleResults;
        this.userName = userName;
        this.tags = tags;
        this.scanName = scanName;
    }
}