Appium Tutorial: How to Automate Mobile Tests [2026]

Learn how to automate mobile tests with Appium in 2026, from setup and desired capabilities to writing, running, and scaling Android and iOS tests.

Written by Sujay Sawant Sujay Sawant
Reviewed by Ashwani Pathak Ashwani Pathak
Last updated: 14 May 2026 33 min read

Key Takeaways

  • Appium automates native, hybrid, and mobile web apps across Android and iOS using one testing approach.
  • Modern Appium works through a server and platform drivers, so correct setup and capabilities matter.
  • Reliable Appium tests need stable locators, explicit waits, separate platform configs, and useful debugging logs.

Appium Tutorial: How to Automate Mobile Tests [2026]

Appium is an open-source framework for automating tests on mobile apps. It lets teams write tests for Android, iOS, and mobile web applications using one automation approach instead of maintaining separate test suites for each platform.

Appium works by sending commands from a test script to the Appium server, which then passes them to platform-specific drivers such as UiAutomator2 for Android and XCUITest for iOS.

In this guide, I’ll explain how Appium works, how to set it up, how to write your first Appium test, how to configure capabilities, use stable locators, handle waits, manage Android and iOS execution, and debug common mobile test failures.

What Can You Automate With Appium?

Appium can automate mobile apps across different app types and platforms. Its main use case is end-to-end mobile UI testing, where the test interacts with the app the way a real user would: tapping buttons, entering text, scrolling screens, switching contexts, and validating results.

Appium is commonly used for three types of mobile applications.

Native mobile apps

Native apps are built specifically for Android or iOS using platform SDKs and frameworks. These are the apps users install from the Play Store or App Store.

With Appium, you can automate native app flows such as:

  • Login and signup
  • Product search
  • Cart and checkout
  • Form submission
  • Push notification flows
  • Permission dialogs
  • Navigation between screens
  • File upload or camera-based flows, depending on device support

For Android native apps, Appium usually works through the UiAutomator2 driver. For iOS native apps, it uses the XCUITest driver.

Hybrid mobile apps

Hybrid apps combine native app shells with web content inside a WebView. Many mobile apps use this model for screens such as checkout, help pages, payment flows, or content-heavy sections.

Appium can automate hybrid apps by switching between:

  • Native context, where it interacts with native app elements
  • WebView context, where it interacts with web elements inside the app

This is useful when a single user journey moves across both native and web-based screens. For example, a user may start on a native login screen, move to a WebView payment page, and return to a native confirmation screen.

Mobile web apps

and Safari on iOS.

This is useful when teams need to validate how a web application behaves on mobile browsers, especially for:

  • Responsive layouts
  • Touch interactions
  • Mobile browser navigation
  • Form behavior
  • Cross-browser mobile issues

Mobile web testing with Appium is different from testing a desktop browser because the test still runs against a real or simulated mobile environment.

How Appium Works in 2026

Appium works as a bridge between your test script and the mobile device. The test script does not talk to the Android or iOS app directly. It sends commands to the Appium server, and the server passes those commands to the right platform driver.

The flow looks like this:

  1. The test script sends a command, such as tap, type, scroll, or find element.
  2. The Appium server receives the command through the WebDriver protocol.
  3. Appium checks which driver is being used for the session.
  4. The driver sends the command to the platform automation framework.
  5. The device performs the action on the app.
  6. The result is sent back to the test script.

For Android, Appium commonly uses the UiAutomator2 driver. For iOS, it uses the XCUITest driver. These drivers are important because Appium itself does not automate the device directly. It depends on platform-specific drivers to understand how Android and iOS expose their UI elements and actions.

A simple way to understand the architecture is:

How Appium Works in 2026

For example, when a test clicks a login button on an Android app, the command starts in the test script. The Appium server receives it, sends it to the UiAutomator2 driver, and the driver uses Android’s automation framework to find and tap the button.

The same idea applies to iOS. The test script sends a command to Appium, Appium routes it to the XCUITest driver, and the driver works with Apple’s testing framework to perform the action on the iPhone simulator or real device.

This driver-based model is important in modern Appium. Older tutorials often describe Appium as one bundled tool where most things are already included. That is no longer the right way to think about it. In current Appium versions, you install Appium first, then install the drivers you need for the platforms you want to test.

For most mobile automation teams, this means:

  • Use UiAutomator2 for Android app testing.
  • Use XCUITest for iOS app testing.
  • Use Espresso only when your Android tests need closer synchronization with app internals.
  • Keep Appium, drivers, and client libraries updated together to avoid compatibility issues.

This structure gives Appium flexibility, but it also means setup matters. If the wrong driver is installed, the capabilities are outdated, or the device environment is not ready, the test may fail before the app even opens.

Appium 1 vs Appium 2 vs Appium 3: What Changed?

Many Appium issues happen because teams follow setup steps written for an older version. This matters because Appium has changed significantly over time.

Appium 1 installed the server and common drivers together. Appium 2 changed that model by making drivers and plugins separate from the core Appium server. This means drivers such as UiAutomator2 and XCUITest must be installed and managed separately. Appium 3 keeps the same driver-based architecture, but removes more legacy behavior and expects a more modern setup.

Note: For new projects, start with Appium 3. Existing Appium 1 or 2 projects do not need to migrate immediately unless the setup is blocked, outdated, or hard to maintain. Migrating from Appium 1 needs more work because Appium 2 changed the driver and server model, while Appium 2 to Appium 3 is usually a smaller upgrade.

AspectAppium 1Appium 2Appium 3What testers should do now
Setup modelMore bundled. Many drivers came with the main Appium installation.Modular. Appium server and drivers are installed separately.Still modular, with more old behavior removed.Install Appium first, then install only the drivers your project needs.
Driver managementDrivers were mostly handled as part of Appium.Drivers are managed using Appium CLI commands.Same driver-based model, but version compatibility matters more.Use commands like appium driver install uiautomator2, appium driver install xcuitest, and appium driver list –installed.
Android automationOlder guides may mention UiAutomator, Selendroid, or bundled Android support.UiAutomator2 and Espresso are installed as separate drivers.UiAutomator2 remains the default choice for most Android UI automation.Use UiAutomator2 for most Android tests. Use Espresso only when the test needs closer synchronization with the app.
iOS automationOlder guides may mention UIAutomation or older bootstrap flows.XCUITest is installed as a separate driver.XCUITest remains the standard driver for iOS automation.Use XCUITest for iOS. Avoid tutorials that mention UIAutomation for modern iOS testing.
Protocol supportSupported older protocols such as JSON Wire Protocol and Mobile JSON Wire Protocol.Removed JSONWP/MJSONWP support and moved to W3C WebDriver only.Continues the W3C-first model with stricter behavior.Use current Appium and Selenium client libraries that support W3C WebDriver.
CapabilitiesMany examples used “Desired Capabilities” without prefixes.Appium-specific capabilities need the appium: vendor prefix.Same approach, with stricter expectations around valid capabilities.Use W3C-style capabilities such as appium:automationName, appium:deviceName, and appium:app.
PluginsLimited plugin-based extension model.Plugins became part of the Appium ecosystem.Plugins remain optional and are used for specialized workflows.Do not add plugins by default. Use them only when the project has a clear need.
Appium DesktopMany tutorials used Appium Desktop for server start and inspection.Appium Inspector became the preferred separate inspection tool.Inspector and CLI-based setup are the safer current path.Use Appium CLI for server and driver management. Use Appium Inspector for locating elements.
Migration effortOlder Appium 1 projects may need larger changes.Appium 2 introduced the biggest setup shift.Appium 3 migration is smaller than Appium 2, but still includes breaking changes.Check Node, npm, client library, driver versions, and capability format before upgrading.
Main risk for testersCopying old examples may still appear to work in some cases, but can fail in modern environments.Missing drivers and unprefixed capabilities are common setup issues.Old endpoints, old clients, and weak capabilities create more launch-time failures.Treat old Appium examples carefully. Update setup, drivers, capabilities, and locators before debugging the test logic.

For most teams, the practical takeaway is simple: do not treat Appium as one bundled mobile testing tool anymore. Install the server, install the right drivers, use W3C capabilities, and keep the Appium server, drivers, and client libraries aligned.

Appium 3 Setup Requirements

Before installing Appium, make sure the local machine is ready for mobile automation. Appium itself is lightweight, but Android and iOS testing depend on platform tools, drivers, SDKs, device access, and the right client libraries.

For Appium 3, the basic server requirements are:

  • macOS, Windows, or Linux
  • Node.js ^20.19.0, ^22.12.0, or >=24.0.0
  • npm 10 or later
  • Appium installed through npm
  • At least one Appium driver installed, such as UiAutomator2 or XCUITest

Appium’s current documentation states that Appium 3 requires newer Node and npm versions, with Node 20.19.0+ and npm 10+ as the baseline.

Version Check

Prerequisites

These are needed regardless of whether you are testing Android or iOS apps:

  • Appium server: Receives commands from the test script and routes them to the correct driver.
  • Appium client library: Lets you write tests in languages such as Java, JavaScript, Python, C#, or Ruby.
  • Appium driver: Connects Appium to the target platform. Without a driver, Appium cannot automate anything.
  • Appium Inspector: Helps inspect mobile elements and identify locators before using them in test scripts.
  • Device environment: A real device, emulator, or simulator where the app will run.

Android setup requirements

For Android automation, you need:

  • Android Studio
  • Android SDK
  • Android Platform Tools
  • Android emulator or real Android device
  • USB debugging enabled for real devices
  • UiAutomator2 driver installed in Appium
  • Java Development Kit if the test framework is written in Java

For most Android test automation, UiAutomator2 should be the default driver. Espresso can be used when the team needs closer synchronization with the app, but UiAutomator2 is the usual starting point for Appium-based Android UI testing.

iOS setup requirements

For iOS automation, you need:

  • macOS
  • Xcode
  • Xcode command-line tools
  • iOS simulator or real iOS device
  • XCUITest driver installed in Appium
  • Apple Developer account setup for real-device testing
  • Code signing and provisioning configured for physical iPhones or iPads

The main difference between Android and iOS setup is that iOS automation depends heavily on Apple’s tooling. Simulators are easier to start with, but real-device iOS testing requires signing, provisioning, and device trust configuration.

What to check before writing tests

A working Appium setup is not just about installing Appium. Before creating the first test, check that:

  • Node and npm versions match Appium 3 requirements.
  • The required Appium driver is installed.
  • The Android emulator, iOS simulator, or real device is visible to the machine.
  • The app build is available as an .apk, .app, .ipa, or installed app package.
  • The test framework has the correct Appium client library.
  • Appium Inspector can detect the app’s UI elements.

How to Install Appium in 2026

Once Node.js, npm, and the platform tools are ready, install Appium from npm. Appium 3 supports macOS, Windows, and Linux, but it requires a recent Node.js and npm version. The current Appium docs list Node ^20.19.0 || ^22.12.0 || >=24.0.0 and npm >=10 as the basic server requirements.

Step 1: Install Appium

Use npm to install Appium globally:

npm install -g appium

This installs the Appium server so you can start it from the command line. Appium’s official install guide also notes that Appium is installed globally using npm.

Install Appium

Step 2: Check the installed Appium version

After installation, verify that Appium is available:

appium -v

This confirms that the Appium CLI is installed and accessible from the terminal.

Appium version

Step 3: Install the Android driver

For Android automation, install the UiAutomator2 driver:

appium driver install uiautomator2

UiAutomator2 is the standard Appium driver for most Android UI automation. Appium does not come bundled with drivers, so installing Appium alone is not enough to automate Android or iOS apps.

Install the Android driver

Step 4: Install the iOS driver

For iOS automation, install the XCUITest driver:

appium driver install xcuitest

This driver is used for iOS automation through Apple’s XCUITest framework. You need macOS and Xcode for iOS testing.

Install the iOS driver

Step 5: Check installed drivers

Use this command to confirm which drivers are installed:

appium driver list --installed

A working Android setup should show uiautomator2. A working iOS setup should show xcuitest.

Driver List

Step 6: Validate the Android setup

For Android, run the driver doctor command:

appium driver doctor uiautomator2

This checks whether the required Android dependencies are configured correctly. Appium’s documentation notes that official drivers include Appium Doctor support to verify driver requirements.

Validate the Android setup

Step 7: Start the Appium server

Start the Appium server from the terminal:

appium

When the server starts, it loads the installed drivers and waits for new session requests from your test scripts. The server log will also show the local URL that your client code can use, commonly:

http://127.0.0.1:4723/

Keep the server running while executing local tests. If a session fails, the Appium server log is one of the first places to check because it usually shows driver, capability, and device connection errors.

Start the Appium server

Understanding Appium Capabilities

Capabilities tell Appium what kind of session to create. They define the platform, driver, device, app, and other session-level settings before the test starts. Once the session is created, these values cannot be changed for that session. Appium follows the W3C WebDriver capabilities model, and Appium-specific capabilities use the appium: prefix.

This matters because many older examples still use unprefixed capabilities such as deviceName or app. In modern Appium, these should be written as appium:deviceName and appium:app.

Common Appium capabilities

CapabilityUsed forExample
platformNameDefines the target platformAndroid, iOS
appium:automationNameDefines the Appium driverUiAutomator2, XCUITest
appium:deviceNameDefines the device or simulator nameAndroid Emulator, iPhone 15
appium:platformVersionDefines the OS version when needed17.0, 14
appium:appPath to the app file/path/to/app.apk, /path/to/app.app
appium:appPackageAndroid app package when app is already installedcom.example.app
appium:appActivityAndroid launch activity.MainActivity
appium:bundleIdiOS bundle identifier when app is already installedcom.example.iosapp

Android capabilities example

Use this when you want Appium to install and launch an APK:

{

 "platformName": "Android",

 "appium:automationName": "UiAutomator2",

 "appium:deviceName": "Android Emulator",

 "appium:app": "/path/to/app.apk"

}

Use this when the Android app is already installed and you want to launch it by package and activity:

{

 "platformName": "Android",

 "appium:automationName": "UiAutomator2",

 "appium:deviceName": "Android Emulator",

 "appium:appPackage": "com.example.app",

 "appium:appActivity": ".MainActivity"

}

iOS capabilities example

Use this when testing an iOS app file on a simulator:

{

 "platformName": "iOS",

 "appium:automationName": "XCUITest",

 "appium:deviceName": "iPhone 15",

 "appium:platformVersion": "17.0",

 "appium:app": "/path/to/app.app"

}

Use this when the iOS app is already installed and you want to launch it by bundle ID:

{

 "platformName": "iOS",

 "appium:automationName": "XCUITest",

 "appium:deviceName": "iPhone 15",

 "appium:platformVersion": "17.0",

 "appium:bundleId": "com.example.iosapp"

}

app vs appPackage, appActivity, and bundleId

Use appium:app when Appium needs to install the app before starting the session. This is common when testing a fresh .apk, .app, or .ipa build.

Use appium:appPackage and appium:appActivity when the Android app is already installed and the test only needs to launch it.

Use appium:bundleId when the iOS app is already installed and the test only needs to launch it.

For most teams, this is the safest rule: use appium:app for fresh build validation, and use package, activity, or bundle ID when the app is already installed as part of the device setup.

Keep capability files separate for Android, iOS simulator, and iOS real-device runs so that one environment does not accidentally break another.

Write Your First Appium Test

Now that Appium, the Android driver, and the Appium server are ready, the next step is to create a basic test. This example uses Java + TestNG + Appium Java Client because it is a common stack for mobile automation teams.

The Appium Java Client is the official Java binding for writing Appium tests and supports driver-specific classes such as AndroidDriver for UiAutomator2 and IOSDriver for XCUITest. The current Maven Central version checked for this example is 10.1.1. TestNG 7.12.0 is used for test annotations and assertions.

Step 1: Add Maven dependencies

Create a Maven project and add the following dependencies in pom.xml.

<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

        https://maven.apache.org/xsd/maven-4.0.0.xsd">


   <modelVersion>4.0.0</modelVersion>


   <groupId>com.example</groupId>

   <artifactId>appium-mobile-tests</artifactId>

   <version>1.0-SNAPSHOT</version>


   <properties>

       <maven.compiler.source>17</maven.compiler.source>

       <maven.compiler.target>17</maven.compiler.target>

       <appium.java.client.version>10.1.1</appium.java.client.version>

       <testng.version>7.12.0</testng.version>

   </properties>


   <dependencies>

       <dependency>

           <groupId>io.appium</groupId>

           <artifactId>java-client</artifactId>

           <version>${appium.java.client.version}</version>

       </dependency>


       <dependency>

           <groupId>org.testng</groupId>

           <artifactId>testng</artifactId>

           <version>${testng.version}</version>

           <scope>test</scope>

       </dependency>

   </dependencies>

</project>

You do not need to add Selenium separately for this basic example. Appium Java Client already depends on Selenium libraries. The Appium Java Client compatibility matrix also shows which Selenium versions align with each Appium Java Client version, so pin Selenium separately only when your framework needs tighter dependency control.

Also Read: What is POM in Maven

Step 2: Start the Appium server

Before running the test, start the Appium server in a separate terminal:

appium

Keep this terminal open. The test will send commands to the local Appium server at:

http://127.0.0.1:4723

Also make sure an Android emulator is running, or a real Android device is connected and visible through:

Real Android device is connected and visible

adb devices

adb devices

Step 3: Create a basic Android test

Create a test file at:

src/test/java/tests/FirstAndroidTest.java

Use this code:

package tests;

import io.appium.java_client.AppiumBy;

import io.appium.java_client.android.AndroidDriver;

import io.appium.java_client.android.options.UiAutomator2Options;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.ui.ExpectedConditions;

import org.openqa.selenium.support.ui.WebDriverWait;

import org.testng.Assert;

import org.testng.annotations.AfterMethod;

import org.testng.annotations.BeforeMethod;

import org.testng.annotations.Test;


import java.net.URI;

import java.net.URL;

import java.time.Duration;

public class FirstAndroidTest {


   private AndroidDriver driver;

   private WebDriverWait wait;


   @BeforeMethod

   public void setUp() throws Exception {

       UiAutomator2Options options = new UiAutomator2Options()

               .setPlatformName("Android")

               .setAutomationName("UiAutomator2")

               .setDeviceName("Android Emulator")

               .setApp(System.getProperty("user.dir") + "/apps/sample.apk");


       URL appiumServerUrl = URI.create("http://127.0.0.1:4723").toURL();


       driver = new AndroidDriver(appiumServerUrl, options);

       wait = new WebDriverWait(driver, Duration.ofSeconds(10));

   }


   @Test

   public void userCanOpenLoginScreen() {

       WebElement loginButton = wait.until(

               ExpectedConditions.elementToBeClickable(

                       AppiumBy.accessibilityId("Login")

               )

       );


       loginButton.click();


       WebElement loginTitle = wait.until(

               ExpectedConditions.visibilityOfElementLocated(

                       AppiumBy.accessibilityId("Login Screen")

               )

       );


       Assert.assertTrue(

               loginTitle.isDisplayed(),

               "Login screen should be visible after tapping Login."

       );

   }


   @AfterMethod(alwaysRun = true)

   public void tearDown() {

       if (driver != null) {

           driver.quit();

       }

   }

}

This example assumes the app has accessibility IDs named Login and Login Screen. In a real project, inspect your app with Appium Inspector and replace these values with locators from your application.

Intellij View

Step 4: Run the test

From the project root, run:

mvn test

If the setup is correct, Maven will compile the test, TestNG will start the test method, and Appium will create a new Android session through the UiAutomator2 driver.

Run the test

What this test does

The test follows the basic Appium execution flow:

  • UiAutomator2Options defines the Android session.
  • setAutomationName(“UiAutomator2”) tells Appium to use the Android UiAutomator2 driver.
  • setApp() points to the APK that should be installed and launched.
  • AndroidDriver starts the session with the Appium server.
  • WebDriverWait waits until the login button is actually clickable.
  • AppiumBy.accessibilityId() locates the mobile element using a stable accessibility identifier.
  • driver.quit() closes the session after the test finishes.

The important part is not just that the test clicks a button. The important part is the structure: setup, session creation, wait, action, assertion, and cleanup. Most Appium tests should follow this shape so failures are easier to debug.

Appium Locator Strategies

Locators decide how Appium finds elements inside the app. A weak locator can make a good test fail even when the app is working correctly. This usually happens when the locator depends on screen position, changing text, or a long XPath tied to the UI hierarchy.

A good Appium locator should be:

  • stable across small UI changes
  • easy for another tester to understand
  • specific enough to avoid matching the wrong element
  • supported by the platform driver being used

1. Accessibility ID

Accessibility ID is usually the best locator for Appium tests. It works across Android and iOS, and it is less dependent on the visual structure of the screen.

driver.findElement(AppiumBy.accessibilityId("Login"));

Use Accessibility ID for important user actions such as login buttons, search fields, menu items, and checkout actions.

For long-term test stability, ask developers to add meaningful accessibility labels or content descriptions to key elements. This helps both automation and app accessibility.

Accessibility ID

 

2. Android resource ID

For Android apps, resource ID is another strong option. It points to the element ID defined in the Android app code.

driver.findElement(AppiumBy.id("com.example:id/login_button"));

This is useful when the app exposes stable Android IDs. Avoid IDs that are generated dynamically or change between builds.

Android resource ID

3. iOS Predicate String

For iOS apps, predicate strings are often more reliable than XPath. They let you locate elements using properties such as label, name, value, type, or visibility.

driver.findElement(

       AppiumBy.iOSNsPredicateString("label == 'Login'")

);

You can also combine conditions:

driver.findElement(

       AppiumBy.iOSNsPredicateString("label == 'Login' AND visible == true")

);

This is useful when the iOS screen has multiple elements with similar labels or when you need a more specific condition.

4. iOS Class Chain

iOS Class Chain is another iOS-specific locator strategy. It is faster and cleaner than XPath for many hierarchy-based lookups.

driver.findElement(

       AppiumBy.iOSClassChain("**/XCUIElementTypeButton[`label == 'Login'`]")

);

Use it when the element cannot be located by Accessibility ID or predicate alone, but avoid making the chain too dependent on exact screen hierarchy.

5. Android UiAutomator selector

Android UiAutomator selectors are useful when you need Android-specific matching logic.

driver.findElement(

       AppiumBy.androidUIAutomator("new UiSelector().text(\"Login\")")

);

You can also use it for scrolling:

driver.findElement(

       AppiumBy.androidUIAutomator(

               "new UiScrollable(new UiSelector().scrollable(true))" +

               ".scrollIntoView(new UiSelector().text(\"Settings\"))"

       )

);

This is useful for Android screens where the element is not immediately visible and needs to be scrolled into view.

6. XPath

XPath should be treated as a fallback in Appium. It can work, but it is often slower and more fragile because it depends on the UI tree.

driver.findElement(

       AppiumBy.xpath("//android.widget.Button[@text='Login']")

);

XPath

Avoid XPath like this:

driver.findElement(

       AppiumBy.xpath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.Button[2]")

);

This type of XPath breaks easily when the layout changes, even if the user-facing screen still looks the same.

Avoid XPath like this

Recommended locator order

For most Appium projects, use this order when choosing locators:

PriorityLocator strategyWhen to use
1Accessibility IDBest default choice for stable cross-platform tests
2Android resource IDStrong option for Android apps with stable IDs
3iOS Predicate StringStrong option for iOS when Accessibility ID is not available
4iOS Class ChainUseful for iOS hierarchy-based lookup
5Android UiAutomatorUseful for Android-specific selection and scrolling
6XPathUse only when better locators are not available

The best locator is not always the shortest one. It is the one that keeps working after small UI changes, app updates, and device differences. For production-level Appium tests, locator stability matters more than quick test authoring.

Handling Waits in Appium

Mobile screens rarely load at the same speed every time. An element may appear after an animation, a network call, a permission dialog, or a WebView load. If the test tries to interact with the element too early, the app may be working correctly, but the test still fails.

This is why waits are important in Appium. They help the test wait for a real app condition before performing the next action.

Why hard waits are a problem

A hard wait pauses the test for a fixed time.

Thread.sleep(5000);

This looks simple, but it creates two problems:

  • If the element appears in one second, the test still waits five seconds.
  • If the element appears after six seconds, the test still fails.

Hard waits make the suite slower and do not solve timing issues reliably. They should be avoided except for rare debugging situations.

Use explicit waits instead

An explicit wait waits until a specific condition is true. For example, wait until a button is clickable before tapping it.

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

WebElement loginButton = wait.until(

       ExpectedConditions.elementToBeClickable(

               AppiumBy.accessibilityId("Login")

       )

);


loginButton.click();

In this example, Appium does not blindly wait for 10 seconds. It waits up to 10 seconds for the login button to become clickable. If the button is ready earlier, the test continues immediately.

Common Appium wait conditions

Use wait conditions based on what the test needs next.

ConditionUse when
visibilityOfElementLocatedThe element should be visible before validation or reading text
elementToBeClickableThe element should be ready for tap or click
presenceOfElementLocatedThe element should exist in the UI tree, even if not visible yet
invisibilityOfElementLocatedA loader, toast, or blocking overlay should disappear
textToBePresentInElementLocatedA label, message, or status should update before assertion

Example for waiting until a loader disappears:

wait.until(

       ExpectedConditions.invisibilityOfElementLocated(

               AppiumBy.accessibilityId("Loading")

       )

);

This is useful after login, payment, search, or any screen that depends on a backend response.

Wait for screen state, not just elements

A common Appium mistake is waiting for one element and assuming the whole screen is ready. In mobile apps, one button may appear before data, images, or dynamic content finishes loading.

Instead, wait for the state that proves the screen is usable.

For example, after tapping login, do not wait only for the next screen container. Wait for a user-specific element that confirms the login completed.

WebElement accountHeader = wait.until(

       ExpectedConditions.visibilityOfElementLocated(

               AppiumBy.accessibilityId("Account Home")

       )

);


Assert.assertTrue(accountHeader.isDisplayed());

This makes the test closer to how a real user would judge whether the action succeeded.

Handle permission dialogs carefully

Permission dialogs are common in mobile testing. They may appear on a fresh install, but not on later runs. A stable test should handle them only when they appear.

try {

   WebElement allowButton = new WebDriverWait(driver, Duration.ofSeconds(3))

           .until(ExpectedConditions.elementToBeClickable(

                   AppiumBy.id("com.android.permissioncontroller:id/permission_allow_button")

           ));


   allowButton.click();

} catch (Exception ignored) {

   // Permission dialog did not appear. Continue the test.

}

Do not add long waits for permission popups in every test. Keep the timeout short and continue if the dialog is not present.

Avoid mixing implicit and explicit waits

Implicit waits apply globally to element searches. Explicit waits are condition-based. Mixing both can make failures harder to understand because Appium may wait longer than expected before throwing an error.

For Appium test suites, a safer approach is:

  • keep implicit waits low or avoid them
  • use explicit waits for important UI conditions
  • create reusable wait helper methods for common screens

Example helper method:

public WebElement waitForVisible(By locator) {

   WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

   return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));

}

Then use it like this:

WebElement loginButton = waitForVisible(AppiumBy.accessibilityId("Login"));

loginButton.click();

Debugging Common Appium Failures

Appium failures usually fall into three areas: setup, locators, or timing. Setup failures happen before the app opens. Locator failures happen when Appium cannot find the expected element. Timing failures happen when the app is working, but the test acts before the screen is ready.

Start debugging from the failure point. If the session does not start, do not inspect test logic yet. Check the driver, device, app path, and capabilities first. If the app opens but the test fails midway, check locators, waits, app state, and screen transitions.

ErrorCommon causeHow to fix
SessionNotCreatedExceptionWrong capabilities, missing driver, unavailable device, incorrect app pathCheck installed drivers, device connection, app file path, and platform version
NoSuchElementExceptionLocator is wrong, element is not visible, screen has not loadedRecheck the element in Appium Inspector and add an explicit wait
StaleElementReferenceExceptionThe screen refreshed and the old element reference is no longer validLocate the element again after navigation or screen update
InvalidElementStateExceptionElement is present but cannot receive the actionCheck whether it is enabled, covered by another element, or blocked by the keyboard
WebDriverAgent erroriOS signing, provisioning, Xcode, or device trust issueCheck Xcode setup, signing team, provisioning profile, and WDA logs
adb device offlineAndroid device connection is unstableRestart adb, reconnect the device, and authorize USB debugging
App launches but test fails immediatelyWrong app state or unexpected permission dialogReset app state or handle permission dialogs before test actions
Test passes locally but fails in CIDifferent device, OS version, timing, or environment dataCapture logs, screenshots, and device details from CI runs

After identifying the error type, check the Appium server logs first. They usually show whether the failure came from driver loading, capability validation, app launch, device connection, or WebDriverAgent setup.

For element-related failures, open the same screen in Appium Inspector and confirm that the locator matches the current UI tree. If the issue is intermittent, capture screenshots and page source on failure so you can compare what the user saw with what Appium could actually detect.

Best Practices for Running Appium Tests on Android and iOS

Running Appium tests on Android and iOS follows the same basic flow, but the platform details are different. A stable Appium suite should keep those differences clear instead of forcing both platforms into one shared setup.

AreaAndroidiOS
Main driverUiAutomator2XCUITest
Local toolingAndroid SDK, adb, emulatormacOS, Xcode, simctl
Real-device setupUSB debugging and device authorizationSigning, provisioning, device trust
App file.apk.app for simulator, .ipa for real device
Installed app launchappPackage and appActivitybundleId
Common setup issueDevice not visible in adbWebDriverAgent or signing failure

Before running Android tests, confirm that the device or emulator is visible:

adb devices

Before running iOS simulator tests, check the available simulators:

xcrun simctl list devices

Keep Android and iOS configuration separate. Even when the user journey is the same, the drivers, app files, device names, and launch capabilities are different.

A clean framework usually stores platform configuration separately:

config/

 android.properties

 ios.properties

or:

config/

 android.json

 ios.json

The test should describe the user flow. The configuration should describe where and how that test runs.

A few practices make Appium tests more stable across both platforms:

  • Prefer Accessibility IDs for important elements because they are less tied to layout changes.
  • Avoid XPath unless the app does not expose better identifiers.
  • Use explicit waits based on visible app state, not hard-coded sleeps.
  • Reset app state deliberately between tests instead of depending on leftovers from the previous session.
  • Keep test data setup outside the UI when possible.
  • Capture Appium logs, device logs, screenshots, and videos for CI failures.
  • Run smoke tests on emulators or simulators, but validate release-critical flows on real devices.
  • Keep Appium, driver, client library, OS, and device versions visible in CI logs.
  • Avoid putting too many user journeys inside one test. One test should verify one clear flow.

When Not to Use Appium

Appium is best for testing important mobile user journeys where the UI, device, and platform behavior matter. It should not be used for every validation because UI tests are slower and more expensive to maintain than lower-level tests.

Avoid using Appium when:

  • Faster test layers can cover the check: Use unit, API, component, or integration tests when the validation does not need the mobile UI.
  • The test needs internal app access: Appium works from the outside like a user, so it is not ideal for private methods, memory state, or internal event handling.
  • The goal is visual comparison: Use a visual testing tool for pixel-level layout, spacing, color, or screenshot diff checks.
  • The screen is still changing often: Wait until the UI has stable identifiers before adding Appium coverage.
  • Speed is the main priority: Appium is useful for end-to-end validation, but it is too heavy for small checks that need fast feedback.
  • The test only validates backend logic: Business rules, data calculations, and API behavior should be tested below the UI.
  • Only emulators or simulators are available for device-heavy flows: Hardware-dependent features should be validated on real devices before release.

Conclusion

Appium in 2026 works best when the setup is current: Appium 3, platform drivers, W3C capabilities, stable locators, and explicit waits. Most early failures come from missing drivers, outdated capability formats, weak locators, or device setup gaps.

Use Appium for mobile flows where the UI, device, and platform behavior affect release quality. Keep Android and iOS configuration separate, capture logs and screenshots for failures, and validate critical flows on real devices before shipping.

Tags
Appium Automation Testing Mobile App Testing

FAQs

Appium is an open-source tool that automates native, hybrid, and mobile web applications on Android and iOS. It allows testers to write scripts in multiple languages and run them on real devices without modifying the app.

Appium uses a client–server model where commands from test scripts are translated by the Appium server into platform-specific automation calls. This architecture enables the same API to work across Android (via UIAutomator/ Espresso) and iOS (via XCUITest).

Installation involves setting up JDK, Node.js, Appium Server, Android SDK for Android testing, and Xcode for iOS testing. After configuring environment variables and verifying dependencies using Appium Doctor, the server can be launched to start creating sessions.

Stable locators, Page Object Model design, explicit waits, modular code, version control, controlled app state, parallel execution, and real-device validation are essential for reducing flakiness and improving test speed.

BrowserStack provides thousands of real Android and iOS devices, instant setup with the BrowserStack SDK, parallel execution, and rich debugging logs—making it easier to scale Appium testing and resolve device-specific issues without maintaining physical hardware.

A standard Appium workflow begins with launching the Appium server, defining desired capabilities, establishing a session with the target device, interacting with UI elements through automation commands, capturing logs or screenshots, and closing the session once the test completes.

Before creating test scripts, Appium requires installation of JDK, Node.js, the Appium server, Android SDK or Xcode (depending on the platform), configuration of environment variables, and verification of dependencies using Appium Doctor to ensure the environment is ready for automation.

Struggling With Flaky Appium Tests?
Run tests in real devices to catch device-specific failures early.