Test Example in C#
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
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Support.UI;
using System;
using System.Collections.Generic;
using System.Threading;
namespace AppiumTests
{
[TestFixture]
public class AndroidDebugTest
{
private AndroidDriver driver;
// The axeSettings object still accepts apiKey and axeServiceUrl but is not required
// if you are going to make one time [OneTimeSetUp] annotated call to 'mobile: axeStartSession'
private Dictionary<string, object> axeSettings;
private const string API_KEY = "<YOUR_API_KEY>";
private const string PROJECT_ID = "<YOUR_DEV_HUB_PROJECT_ID>";
private const string APP_PACKAGE = "<YOUR_APP_PACKAGE_NAME>";
// It is very important to make a call to 'mobile: axeStartSession' inside [OneTimeSetUp] annotated
// method to setup your session for posting to Dev Hub.
[OneTimeSetUp]
public void SetUp()
{
axeSettings = new Dictionary<string, object>
{
{ "tags", new[] { "appium", "qa" } },
{ "uploadToDashboard", true },
{ "ignoreRules", new[] { "ScreenOrientation" } }
};
var options = new AppiumOptions();
options.PlatformName = "Android";
options.AddAdditionalAppiumOption("deviceName", "Android");
options.AddAdditionalAppiumOption("appPackage", APP_PACKAGE);
options.AddAdditionalAppiumOption("appActivity", ".MainActivity");
options.AddAdditionalAppiumOption("automationName", "AxeUiAutomator2");
options.AddAdditionalAppiumOption("uiautomator2ServerLaunchTimeout", 60000);
options.AddAdditionalAppiumOption("uiautomator2ServerInstallTimeout", 60000);
options.AddAdditionalAppiumOption("adbExecTimeout", 60000);
options.AddAdditionalAppiumOption("ignoreHiddenApiPolicyError", true);
options.AddAdditionalAppiumOption("disableWindowAnimation", true);
options.AddAdditionalAppiumOption("waitForIdle", true);
options.AddAdditionalAppiumOption("commandTimeout", 300);
options.AddAdditionalAppiumOption("noReset", false);
options.AddAdditionalAppiumOption("fullReset", false);
driver = new AndroidDriver(new Uri("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.
var axeAuthSettings = new Dictionary<string, object>
{
{ "apiKey", API_KEY },
{ "projectId", PROJECT_ID }
};
driver.ExecuteScript("mobile: axeStartSession", axeAuthSettings);
}
[OneTimeTearDown]
public void TearDown()
{
if (driver != null)
{
driver.Quit();
}
}
private void DismissSystemUIError()
{
try
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
var systemPopup = wait.Until(drv =>
drv.FindElement(By.XPath("//*[contains(@text, 'System UI')]")));
if (systemPopup.Displayed)
{
var waitButton = wait.Until(drv =>
drv.FindElement(By.XPath("//*[contains(@text, 'Wait')]")));
waitButton.Click();
Console.WriteLine("Dismissed the System UI error popup by clicking 'Wait'.");
}
}
catch (Exception)
{
Console.WriteLine("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)
{
Console.WriteLine($"App was not running or failed to terminate: {e.Message}");
}
driver.ActivateApp(APP_PACKAGE);
try
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
wait.Until(drv => drv.FindElement(By.XPath("//*[contains(@text, 'Screen Name')]")));
}
catch (Exception)
{
DismissSystemUIError();
// Try again after dismissing the error (if not, then fail)
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
wait.Until(drv => drv.FindElement(By.XPath("//*[contains(@text, 'Screen Name')]")));
}
}
[SetUp]
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()
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
var element = wait.Until(drv =>
drv.FindElement(By.XPath("//*[contains(@text, 'Screen Name')]")));
Console.WriteLine(element.Text);
var appiumScanResult = (Dictionary<string, object>)driver.ExecuteScript("mobile: axeScan", axeSettings);
var results = (List<object>)appiumScanResult["axeRuleResults"];
Console.WriteLine($"debug: Total results: {results.Count}");
}
[Test, Order(2)]
public void Test2()
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
var element = wait.Until(drv =>
drv.FindElement(By.XPath("//*[contains(@text, 'Screen Name')]")));
element.Click();
driver.FindElement(By.XPath("//*[contains(@text, 'announced by a screen')]"));
var appiumScanResult = (Dictionary<string, object>)driver.ExecuteScript("mobile: axeScan", axeSettings);
var results = (List<object>)appiumScanResult["axeRuleResults"];
Console.WriteLine($"debug: Total results: {results.Count}");
}
}
}
Full Example with XCUITest
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.iOS;
using OpenQA.Selenium.Support.UI;
using System;
using System.Collections.Generic;
using System.Threading;
namespace AppiumTests
{
[TestFixture]
public class IOSMapsDebugTest
{
private IOSDriver driver;
// The axeSettings object still accepts apiKey and axeServiceUrl but is not required
// if you are going to make one time [OneTimeSetUp] annotated call to 'mobile: axeStartSession'
private Dictionary<string, object> axeSettings;
private const string API_KEY = "<YOUR_API_KEY>";
private const string PROJECT_ID = "<YOUR_DEV_HUB_PROJECT_ID>";
private const string BUNDLE_ID = "com.apple.Maps";
// It is very important to make a call to 'mobile: axeStartSession' inside [OneTimeSetUp] annotated
// method to setup your session for posting to Dev Hub.
[OneTimeSetUp]
public void SetUp()
{
axeSettings = new Dictionary<string, object>
{
{ "tags", new[] { "appium", "qa", "ios" } },
{ "uploadToDashboard", true },
{ "ignoreRules", new[] { "ScreenOrientation" } }
};
// Matching the working sample - minimal capabilities
var options = new AppiumOptions();
options.PlatformName = "iOS";
options.AddAdditionalAppiumOption("automationName", "AxeXCUITest");
options.AddAdditionalAppiumOption("udid", "<YOUR_DEVICE_OR_SIMULATOR_UDID>");
options.AddAdditionalAppiumOption("bundleId", BUNDLE_ID);
options.AddAdditionalAppiumOption("wdaLaunchTimeout", 960000); // 16 minutes like the working sample
// NOT specifying platformVersion - let it auto-detect
driver = new IOSDriver(new Uri("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.
var axeAuthSettings = new Dictionary<string, object>
{
{ "apiKey", API_KEY },
{ "projectId", PROJECT_ID },
{ "axeServiceUrl", "https://mobile-qa.dequelabs.com" }
};
driver.ExecuteScript("mobile: axeStartSession", axeAuthSettings);
}
[OneTimeTearDown]
public void TearDown()
{
if (driver != null)
{
Thread.Sleep(1000);
driver.Quit();
}
}
private void LaunchApp()
{
driver.ActivateApp(BUNDLE_ID);
}
[SetUp]
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(10000)]
public void Test1_ScanMainScreen()
{
var appiumScanResult = (Dictionary<string, object>)driver.ExecuteScript("mobile: axeScan", axeSettings);
var results = (List<object>)appiumScanResult["axeRuleResults"];
Console.WriteLine($"debug: Total results: {results.Count}");
}
[Test, Order(2), Timeout(10000)]
public void Test2_ClickSearchAndScan()
{
var appiumScanResult = (Dictionary<string, object>)driver.ExecuteScript("mobile: axeScan", axeSettings);
var results = (List<object>)appiumScanResult["axeRuleResults"];
Console.WriteLine($"debug: Total results: {results.Count}");
}
}
}
