Appium with Java

Link to Appium with Java copied to clipboard
tip

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?

Before running the Appium test, ensure the apk being passed into Appium runs axe DevTools on the screens you want to test with axe.showA11yFab(activity) and is connected to the dashboard with axe.connect(...). Check out our guide to setting up your app for Appium testing.

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

Setup

Not using Maven Project? View the example on the setup portion of the Setup for Appium Testing.

pom.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.deque.android.example</groupId>
    <artifactId>Java8App</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <repositories>
        <repository>
            <id>mavenCentral</id>
            <url>https://repo1.maven.org/maven2/</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.deque.android</groupId>
            <artifactId>axe-devtools-android-core</artifactId>
            <version>4.1.0</version>
        </dependency>

        <dependency>
            <groupId>io.appium</groupId>
            <artifactId>java-client</artifactId>
            <version>8.1.1</version>
        </dependency>
    </dependencies>
</project>

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;
    }
}