Test Example in Java
Not for use with personal data
Be sure to check out the full Appium setup guide with axe DevTools Mobile if you're just getting started, or more examples of axe DevTools Mobile for Appium in other languages.
Full Example with UIAutomator2
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class AndroidDebugTest {
private static AndroidDriver driver;
// The axeSettings object still accepts apiKey and axeServiceUrl but is not required
// if you are going to make one time @BeforeAll annotated call to 'mobile: axeStartSession'
private static Map<String, Object> axeSettings;
private static final String API_KEY = "<YOUR_API_KEY>";
private static final String PROJECT_ID = "<YOUR_DEV_HUB_PROJECT_ID>";
private static final String APP_PACKAGE = "<YOUR_APP_PACKAGE_NAME>";
// It is very important to make a call to 'mobile: axeStartSession' inside @BeforeAll annotated
// method to setup your session for posting to Dev Hub.
@BeforeAll
public static void setUp() throws MalformedURLException {
axeSettings = new HashMap<>();
axeSettings.put("tags", new String[]{"appium", "qa"});
axeSettings.put("uploadToDashboard", true);
axeSettings.put("ignoreRules", new String[]{"ScreenOrientation"});
UiAutomator2Options options = new UiAutomator2Options()
.setPlatformName("Android")
.setDeviceName("Android")
.setAppPackage(APP_PACKAGE)
.setAppActivity(".MainActivity")
.setAutomationName("AxeUiAutomator2")
.setUiautomator2ServerLaunchTimeout(Duration.ofMillis(60000))
.setUiautomator2ServerInstallTimeout(Duration.ofMillis(60000))
.setAdbExecTimeout(Duration.ofMillis(60000))
.setIgnoreHiddenApiPolicyError(true)
.setDisableWindowAnimation(true)
.setCapability("waitForIdle", true)
.setNewCommandTimeout(Duration.ofSeconds(300))
.setNoReset(false)
.setFullReset(false);
driver = new AndroidDriver(new URL("http://localhost:4723/"), options);
// Make one time call to setup your session for posting to Dev Hub.
// This also accepts axeServiceUrl in case of private instance.
Map<String, Object> axeAuthSettings = new HashMap<>();
axeAuthSettings.put("apiKey", API_KEY);
axeAuthSettings.put("projectId", PROJECT_ID);
driver.executeScript("mobile: axeStartSession", axeAuthSettings);
}
@AfterAll
public static void tearDown() {
if (driver != null) {
driver.quit();
}
}
private void dismissSystemUIError() {
try {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
WebElement systemPopup = wait.until(ExpectedConditions.presenceOfElementLocated(
By.xpath("//*[contains(@text, 'System UI')]")));
if (systemPopup.isDisplayed()) {
WebElement waitButton = wait.until(ExpectedConditions.presenceOfElementLocated(
By.xpath("//*[contains(@text, 'Wait')]")));
waitButton.click();
System.out.println("Dismissed the System UI error popup by clicking 'Wait'.");
}
} catch (Exception e) {
System.out.println("No System UI error popup appeared.");
}
}
private void launchApp() {
try {
driver.terminateApp(APP_PACKAGE);
Thread.sleep(1000); // Give the app time to fully terminate
} catch (Exception e) {
System.out.println("App was not running or failed to terminate: " + e.getMessage());
}
driver.activateApp(APP_PACKAGE);
try {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
wait.until(ExpectedConditions.presenceOfElementLocated(
By.xpath("//*[contains(@text, 'Screen Name')]")));
} catch (Exception e) {
dismissSystemUIError();
// Try again after dismissing the error (if not, then fail)
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
wait.until(ExpectedConditions.presenceOfElementLocated(
By.xpath("//*[contains(@text, 'Screen Name')]")));
}
}
@BeforeEach
public void beforeEach() {
launchApp();
}
// Now since your session is authenticated you can keep making 'mobile: axeScan' call
// like below in test1 and test2. The scans will be uploaded to the dashboard and also grouped in Dev Hub.
@Test
@Order(1)
public void test1() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(
By.xpath("//*[contains(@text, 'Screen Name')]")));
System.out.println(element.getText());
Map<String, Object> appiumScanResult = (Map<String, Object>) driver.executeScript("mobile: axeScan", axeSettings);
List<?> results = (List<?>) appiumScanResult.get("axeRuleResults");
System.out.println("debug: Total results: " + results.size());
}
@Test
@Order(2)
public void test2() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(
By.xpath("//*[contains(@text, 'Screen Name')]")));
element.click();
driver.findElement(By.xpath("//*[contains(@text, 'announced by a screen')]"));
Map<String, Object> appiumScanResult = (Map<String, Object>) driver.executeScript("mobile: axeScan", axeSettings);
List<?> results = (List<?>) appiumScanResult.get("axeRuleResults");
System.out.println("debug: Total results: " + results.size());
}
}
Full Example with XCUITest
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.ios.options.XCUITestOptions;
import org.junit.jupiter.api.*;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class IOSMapsDebugTest {
private static IOSDriver driver;
// The axeSettings object still accepts apiKey and axeServiceUrl but is not required
// if you are going to make one time @BeforeAll annotated call to 'mobile: axeStartSession'
private static Map<String, Object> axeSettings;
private static final String API_KEY = "<YOUR_API_KEY>";
private static final String PROJECT_ID = "<YOUR_DEV_HUB_PROJECT_ID>";
private static final String BUNDLE_ID = "com.apple.Maps";
// It is very important to make a call to 'mobile: axeStartSession' inside @BeforeAll annotated
// method to setup your session for posting to Dev Hub.
@BeforeAll
public static void setUp() throws MalformedURLException {
axeSettings = new HashMap<>();
axeSettings.put("tags", new String[]{"appium", "qa", "ios"});
axeSettings.put("uploadToDashboard", true);
axeSettings.put("ignoreRules", new String[]{"ScreenOrientation"});
// Matching the working sample - minimal capabilities
XCUITestOptions options = new XCUITestOptions()
.setPlatformName("iOS")
.setAutomationName("AxeXCUITest")
.setUdid("<YOUR_DEVICE_OR_SIMULATOR_UDID>")
.setBundleId(BUNDLE_ID)
.setWdaLaunchTimeout(Duration.ofMillis(960000)); // 16 minutes like the working sample
// NOT specifying platformVersion - let it auto-detect
driver = new IOSDriver(new URL("http://127.0.0.1:4723/"), options);
// Make one time call to setup your session for posting to Dev Hub.
// This also accepts axeServiceUrl in case of private instance.
Map<String, Object> axeAuthSettings = new HashMap<>();
axeAuthSettings.put("apiKey", API_KEY);
axeAuthSettings.put("projectId", PROJECT_ID);
axeAuthSettings.put("axeServiceUrl", "https://mobile-qa.dequelabs.com");
driver.executeScript("mobile: axeStartSession", axeAuthSettings);
}
@AfterAll
public static void tearDown() {
if (driver != null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
driver.quit();
}
}
private void launchApp() {
driver.activateApp(BUNDLE_ID);
}
@BeforeEach
public void beforeEach() {
launchApp();
}
// Now since your session is authenticated you can keep making 'mobile: axeScan' call
// like below in test1 and test2. The scans will be uploaded to the dashboard and also grouped in Dev Hub.
@Test
@Order(1)
@Timeout(10)
public void test1_scanMainScreen() {
Map<String, Object> appiumScanResult = (Map<String, Object>) driver.executeScript("mobile: axeScan", axeSettings);
List<?> results = (List<?>) appiumScanResult.get("axeRuleResults");
System.out.println("debug: Total results: " + results.size());
}
@Test
@Order(2)
@Timeout(10)
public void test2_clickSearchAndScan() {
Map<String, Object> appiumScanResult = (Map<String, Object>) driver.executeScript("mobile: axeScan", axeSettings);
List<?> results = (List<?>) appiumScanResult.get("axeRuleResults");
System.out.println("debug: Total results: " + results.size());
}
}
note
Using TestNG? No problem. Our Appium drivers are compatible with this testing framework.
