SoftwareTestPilot
Automation TestingPublished: Updated: · 5 days ago32 min read

Appium Mobile Testing: A to Z Guide (2026)

The 2026 complete Appium tutorial for mobile testing. Step-by-step setup, locators, touch actions, Page Object Model, Appium 2.0, cloud testing with BrowserStack, and CI/CD for Android and iOS.

Avinash Kamble
Avinash Kamble
Founder & QA Engineer at SoftwareTestPilot
Reviewed by Priyanka G.
Share:XLinkedInWhatsApp
Appium 2.0 mobile testing A to Z guide cover — Android and iOS automation with code samples.
Appium 2.0 mobile testing A to Z guide cover — Android and iOS automation with code samples.
In this article
  1. 1. Why Appium in 2026
  2. 2. Appium Architecture
  3. 3. Install Appium Step by Step
  4. 4. Your First Appium Test on Android
  5. 5. Your First Appium Test on iOS
  6. 6. Mobile Locators Deep Dive
  7. 7. Touch Actions and Gestures
  8. 8. Page Object Model for Mobile
  9. 9. Appium 2.0 New Features
  10. 10. Hybrid and Web Apps
  11. 11. Cloud Testing with BrowserStack and Sauce Labs
  12. 12. CI/CD Integration Patterns
  13. 13. Appium vs Alternatives
  14. 14. Mobile Testing Best Practices for 2026
  15. 16. Author Bio & Next Steps
  16. Frequently asked questions

What you'll master: By the end of this Appium tutorial you will have a working Appium 2.0 setup for both Android and iOS, locators and touch actions mastered, the Page Object Model pattern adapted for mobile, parallel execution on real device clouds, and a CI pipeline that ships mobile tests on every PR.

1. Why Appium in 2026

Mobile is the dominant platform for software in 2026. Every product has a mobile app or a responsive web view that behaves like one. And the QA engineer who can test that experience — across devices, OS versions, and form factors — is in high demand.

Appium is the W3C WebDriver-based mobile automation framework. It is the de facto open-source standard for native, hybrid, and mobile-web testing, with bindings in Java, Python, JavaScript, C#, and Ruby.

The reasons Appium dominates in 2026:

  • W3C standard — the same protocol as Selenium, so the patterns transfer
  • Multi-language — one codebase, many language bindings
  • Cross-platform — one API for Android and iOS
  • No app modification required — test the app as-is, no SDK injection
  • Cloud-friendly — works on BrowserStack, Sauce Labs, LambdaTest

The trade-offs: slower than Espresso/XCTest (because of the WebDriver bridge), no native gesture DSL, and a heavier setup than some cloud-only alternatives.

🚀 Skip the setup pain. SoftwareTestPilot Pro members get ready-to-run Appium starter kits for Android and iOS, with parallel cloud execution configured. Start free trial →

2. Appium Architecture

┌─────────────────────────────────────────────┐
│   Your test code (Java/Python/JS)           │
└──────────────────┬──────────────────────────┘
                   │ Appium Client Library
                   ▼
┌─────────────────────────────────────────────┐
│   Appium Server (Node.js)                   │
│   - Listens on port 4723                    │
│   - Speaks W3C WebDriver                    │
│   - Routes to platform drivers              │
└──────────┬───────────────────┬───────────────┘
           │                   │
           ▼                   ▼
┌──────────────────┐ ┌──────────────────┐
│ UiAutomator2     │ │ XCUITest         │
│ (Android driver) │ │ (iOS driver)     │
└──────────────────┘ └──────────────────┘

Key components

  • Appium Server — Node.js process that orchestrates the WebDriver session
  • Driver plugins — UiAutomator2 (Android), XCUITest (iOS), Espresso (Android), UIAutomation (deprecated)
  • Appium Client — language-specific library (java-client, python-client, webdriverio)
  • Appium Inspector — GUI tool to inspect app elements and generate locators

The W3C WebDriver connection

Appium speaks the same W3C WebDriver protocol as Selenium. This means:

  • Same concepts: capabilities, sessions, locators
  • Same patterns: explicit waits, POM
  • Same cloud integration: BrowserStack, Sauce Labs

For Selenium WebDriver basics, see our Selenium WebDriver Guide.

3. Install Appium Step by Step

Prerequisites

  • Node.js 18 LTS or 20 LTS
  • Java 17+ (for Android)
  • Xcode 15+ (for iOS, macOS only)
  • Android Studio (for Android SDK and emulator)
  • Homebrew (macOS) or apt (Linux)

Step 1 — Install the Appium server

npm install -g appium
appium --version  # should show 2.x

Step 2 — Install the drivers

appium driver install uiautomator2

appium driver install xcuitest

appium driver list --installed

Step 3 — Install the client library

Choose your language and install the matching client.

Java (Maven):

<dependency>
  <groupId>io.appium</groupId>
  <artifactId>java-client</artifactId>
  <version>9.4.0</version>
</dependency>

Python:

pip install Appium-Python-Client

JavaScript:

npm install appium @appium/webdriverio

Step 4 — Set up an Android emulator

Use Android Studio's AVD Manager. Create a Pixel 7 with API 34. Boot it once to verify.

Step 5 — Set up an iOS simulator

On macOS only:

xcrun simctl list devices available
xcrun simctl boot "iPhone 15"

Step 6 — Install Appium Inspector

npm install -g appium-inspector
appium-inspector

The Inspector is a desktop app that connects to Appium and lets you visually inspect your app's UI to generate locators.

Step 7 — Start the Appium server

appium

You should see: [Appium] Welcome to Appium v2.x. The server is now listening on port 4723.

4. Your First Appium Test on Android

We'll use the ApiDemos app that ships with Android Studio.

Java

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.net.URL;
import java.time.Duration;

public class FirstAndroidTest {

    public static void main(String[] args) throws Exception {
        UiAutomator2Options options = new UiAutomator2Options()
            .setDeviceName("Pixel_7_API_34")
            .setApp("/path/to/ApiDemos.apk")
            .setAutomationName("UiAutomator2");

        AndroidDriver driver = new AndroidDriver(
            new URL("http://127.0.0.1:4723"), options
        );

        try {
            // Tap "App" → "Alert Dialogs" → "OK"
            driver.findElement(By.xpath("//*[@text='App']")).click();
            driver.findElement(By.xpath("//*[@text='Alert Dialogs']")).click();
            driver.findElement(By.xpath("//*[@text='OK']")).click();

            // Verify the dialog appeared
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
            WebElement okButton = wait.until(
                ExpectedConditions.presenceOfElementLocated(
                    By.xpath("//*[@text='OK']")
                )
            );
            System.out.println("Test passed: " + okButton.getText());

        } finally {
            driver.quit();
        }
    }
}

Python

from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

options = UiAutomator2Options()
options.device_name = "Pixel_7_API_34"
options.app = "/path/to/ApiDemos.apk"

driver = webdriver.Remote("http://127.0.0.1:4723", options=options)

try:
    driver.find_element(AppiumBy.XPATH, "//*[@text='App']").click()
    driver.find_element(AppiumBy.XPATH, "//*[@text='Alert Dialogs']").click()
    driver.find_element(AppiumBy.XPATH, "//*[@text='OK']").click()

    ok_button = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((AppiumBy.XPATH, "//*[@text='OK']"))
    )
    print(f"Test passed: {ok_button.text}")
finally:
    driver.quit()

Run the test:

mvn test
pytest test_first_android.py

For Selenium-style wait patterns adapted to mobile, see our Selenium wait strategies guide.

5. Your First Appium Test on iOS

For iOS you need a macOS host with Xcode installed. We'll use the XCTest sample app.

from appium import webdriver
from appium.options.ios import XCUITestOptions
from appium.webdriver.common.appiumby import AppiumBy

options = XCUITestOptions()
options.device_name = "iPhone 15"
options.platform_version = "17.0"
options.app = "/path/to/TestApp.app"

driver = webdriver.Remote("http://127.0.0.1:4723", options=options)

try:
    # Compute 2 + 3 = 5
    driver.find_element(AppiumBy.ACCESSIBILITY_ID, "IntegerA").send_keys("2")
    driver.find_element(AppiumBy.ACCESSIBILITY_ID, "IntegerB").send_keys("3")
    driver.find_element(AppiumBy.ACCESSIBILITY_ID, "ComputeSumButton").click()

    result = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "Answer").text
    assert result == "5", f"Expected 5, got {result}"
    print(f"Test passed: {result}")
finally:
    driver.quit()

⚠️ iOS apps must be built for testing. If you don't have a .app file, use the freely available Appium iOS Test App from GitHub.

6. Mobile Locators Deep Dive

Mobile apps don't have HTML. They have native UI elements. The locator strategy is different but the principles are the same.

Locator priority (mobile)

PriorityLocatorWhen to use
1accessibility idAlways preferred — explicitly set for testing
2predicate string (iOS) / app:android=new UiSelector().resourceId(...) (Android)When IDs are stable
3xpathLast resort; brittle
4-ios class chain / -android class chainFor complex hierarchies
5id (resource-id on Android)Stable across builds

Accessibility IDs (the right default)

Set accessibility IDs in your app's source code:

// iOS (Swift)
button.accessibilityIdentifier = "submit-button"
// Android (Kotlin)
submitButton.contentDescription = "submit-button"

In tests:

driver.find_element(AppiumBy.ACCESSIBILITY_ID, "submit-button").click()

Android UiAutomator2 selectors

driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.example:id/submit")').click()

driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textContains("Submit")').click()

driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().className("android.widget.Button").index(0)').click()

iOS predicate strings

driver.find_element(AppiumBy.IOS_PREDICATE, 'label == "Submit"').click()

driver.find_element(AppiumBy.IOS_PREDICATE, 'label CONTAINS "Submit"').click()

driver.find_element(AppiumBy.IOS_PREDICATE, 'type == "XCUIElementTypeButton" AND label == "Submit"').click()

iOS class chains

driver.find_element(AppiumBy.IOS_CLASS_CHAIN, '**/XCUIElementTypeButton[`label == "Submit"`]').click()

For more locator patterns, see our Selenium locator guide.

7. Touch Actions and Gestures

Mobile is gesture-driven. Appium provides the W3C Actions API for touch, plus a deprecated TouchAction API still useful for older code.

Tap

driver.find_element(AppiumBy.ACCESSIBILITY_ID, "submit").click()

driver.tap([(540, 1200)])  # tap at (x=540, y=1200)

Long press

from selenium.webdriver.common.action_chains import ActionChains

element = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "menu-item")
actions = ActionChains(driver)
actions.click_and_hold(element).pause(1).release().perform()

Swipe (using W3C Actions)

from appium.webdriver.extensions.action_helpers import ActionHelpers

def swipe_up(driver):
    driver.swipe(540, 1500, 540, 500, 500)

def swipe_left(driver):
    driver.swipe(900, 1200, 100, 1200, 500)

Scroll (the most common mobile gesture)

def scroll_to(driver, text):
    driver.find_element(
        AppiumBy.ANDROID_UIAUTOMATOR,
        f'new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text("{text}"))'
    )

def scroll_to_ios(driver, predicate):
    driver.execute_script('mobile:scroll', {'direction': 'down', 'predicateString': predicate})

Pinch and zoom (advanced)

actions = ActionChains(driver)
actions.add_action(input_)  # build multi-touch

For more gesture patterns and a swipe helper library, see our mobile gestures guide.

8. Page Object Model for Mobile

The Page Object Model works exactly the same for mobile as for web. The class name is the screen name; the methods are user actions.

Login screen (Android)

public class LoginScreen {
    private AndroidDriver driver;

    @AndroidFindBy(accessibility = "email-field")
    private WebElement emailField;

    @AndroidFindBy(accessibility = "password-field")
    private WebElement passwordField;

    @AndroidFindBy(accessibility = "submit-button")
    private WebElement submitButton;

    public LoginScreen(AndroidDriver driver) {
        this.driver = driver;
        PageFactory.initElements(new AppiumFieldDecorator(driver), this);
    }

    public HomeScreen loginAs(String email, String password) {
        emailField.clear();
        emailField.sendKeys(email);
        passwordField.clear();
        passwordField.sendKeys(password);
        submitButton.click();
        return new HomeScreen(driver);
    }
}

Base screen (shared logic)

public abstract class BaseScreen {
    protected AndroidDriver driver;
    protected WebDriverWait wait;

    public BaseScreen(AndroidDriver driver) {
        this.driver = driver;
        this.wait = new WebDriverWait(driver, Duration.ofSeconds(15));
    }

    public abstract boolean isLoaded();

    public void waitForLoaded() {
        wait.until(d -> isLoaded());
    }
}

Test using POM

@Test
public void loginFlow() {
    LoginScreen login = new LoginScreen(driver);
    login.waitForLoaded();

    HomeScreen home = login.loginAs("admin@example.com", "Sup3rSecret!");
    assertTrue(home.isLoaded());
}

For the full POM pattern with WebDriver and Java, see our Selenium POM guide.

9. Appium 2.0 New Features

Appium 2.0 (released April 2023) is the 2026 standard. The big shift: driver plugins instead of bundled drivers.

Key improvements

  • Driver managementappium driver install instead of --default-capabilities magic
  • Better protocol support — BiDi protocol support added
  • No more Appium Doctor — replaced by appium doctor command
  • Multiple drivers per session — run Espresso + UiAutomator2 in the same test
  • Improved error messages — actionable stack traces
  • Better parallel — each driver is independently versioned

Espresso driver (Android fast path)

For purely native Android apps, use the Espresso driver — it's 5–10× faster than UiAutomator2:

options = EspressoOptions()
options.app = "/path/to/app.apk"
options.app_activity = "com.example.MainActivity"

The trade-off: Espresso only sees your app's UI, not other apps or system UI. Use it for fast in-app tests; use UiAutomator2 for cross-app tests.

For a comparison of mobile test drivers, see our mobile testing strategy guide.

10. Hybrid and Web Apps

Hybrid apps (Cordova, Ionic, React Native WebView) and mobile web apps can be tested through the Chrome or Safari driver inside Appium.

Mobile Chrome (Android)

from appium.options.android import ChromeOptions

options = ChromeOptions()
options.device_name = "Pixel_7_API_34"
options.browser_name = "Chrome"

driver = webdriver.Remote("http://127.0.0.1:4723", options=options)
driver.get("https://example.com")

Mobile Safari (iOS)

from appium.options.ios import SafariOptions

options = SafariOptions()
options.device_name = "iPhone 15"
options.platform_version = "17.0"

driver = webdriver.Remote("http://127.0.0.1:4723", options=options)
driver.get("https://example.com")

For hybrid apps, switch between NATIVE_APP and WEBVIEW contexts:

contexts = driver.contexts

driver.switch_to.context(contexts[1])

driver.find_element(By.ID, "submit").click()

driver.switch_to.context(contexts[0])

11. Cloud Testing with BrowserStack and Sauce Labs

Running tests against real devices in a cloud grid is the 2026 default. It gives you 100+ device/OS combos without maintaining your own device farm.

BrowserStack

options = UiAutomator2Options()
options.device_name = "Google Pixel 7"
options.platform_version = "14.0"
options.app = "bs://<your-app-hash>"  # upload to BrowserStack first
options.set_capability("bstack:options", {
    "userName": "your_username",
    "accessKey": "your_access_key",
    "projectName": "QA Regression",
    "buildName": "1.0.0",
    "sessionName": "Login test",
})

driver = webdriver.Remote(
    "https://hub-cloud.browserstack.com/wd/hub",
    options=options
)

Sauce Labs

options.set_capability("sauce:options", {
    "username": "your_username",
    "accessKey": "your_access_key",
    "name": "Login test",
    "build": "1.0.0",
})

driver = webdriver.Remote(
    "https://ondemand.us-west-1.saucelabs.com:443/wd/hub",
    options=options
)

Parallel execution on cloud

import pytest

@pytest.mark.parametrize("device", [
    {"deviceName": "Google Pixel 7", "platformVersion": "14.0"},
    {"deviceName": "Samsung Galaxy S23", "platformVersion": "13.0"},
    {"deviceName": "Google Pixel 4", "platformVersion": "12.0"},
])
def test_login_across_devices(driver, device):
    # driver is provided by pytest plugin, one session per param
    login = LoginScreen(driver)
    login.waitForLoaded()
    home = login.loginAs("admin@example.com", "Sup3rSecret!")
    assert home.isLoaded()

For more cloud testing patterns, see our BrowserStack integration guide.

12. CI/CD Integration Patterns

GitHub Actions (Android emulator)

name: Android Mobile Tests
on: [push, pull_request]
jobs:
  android:
    runs-on: macos-latest  # macOS has fastest Android emulator
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: '17' }
      - run: npm install -g appium
      - run: appium driver install uiautomator2
      - name: Start emulator
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: 34
          arch: x86_64
          profile: pixel_7
          script: ./gradlew test
      - run: mvn -B test -Dtest=MobileTest

GitLab CI with BrowserStack

mobile-tests:
  image: maven:3.9-eclipse-temurin-17
  script:
    - mvn -B test -Dbs.user=$BROWSERSTACK_USER -Dbs.key=$BROWSERSTACK_KEY
  artifacts:
    when: always
    paths: [target/surefire-reports/]

For more CI patterns, see our mobile testing CI/CD guide.

13. Appium vs Alternatives

DimensionAppiumEspressoXCUITestMaestroDetox
Cross-platformYesAndroid onlyiOS onlyYesReact Native only
LanguagesManyKotlin/JavaSwift/Obj-CYAMLJS
SpeedMediumFastestFastestFastFast
Setup complexityHighLowLowVery lowMedium
Cloud-friendlyExcellentLimitedLimitedGoodGood
WebDriver standardYesNoNoNoNo
Best forCross-platform teamsPure AndroidPure iOSSmoke testsReact Native

The 2026 rule of thumb:

  • Cross-platform team + needs WebDriver → Appium
  • Pure Android, deep coverage → Espresso for in-app + Appium for cross-app
  • Pure iOS, deep coverage → XCUITest for in-app + Appium for cross-app
  • React Native → Detox or Maestro
  • Quick smoke tests → Maestro (YAML is faster to write)

For more on mobile framework selection, see our mobile testing tools comparison.

14. Mobile Testing Best Practices for 2026

Do

  • Set accessibility IDs in the app source — they are the most stable locator
  • Test on real devices for the most realistic results
  • Use cloud grids to cover the device/OS matrix
  • Use Espresso (Android) or XCUITest (iOS) for in-app performance tests
  • Use Appium for cross-app and cross-platform tests
  • Reset app state between tests (clear data, kill, relaunch)
  • Capture screenshots on failure for debugging
  • Test on the slowest, oldest device in your supported matrix

Don't

  • Don't use XPath as your default — it's brittle on mobile
  • Don't test gestures with hard-coded coordinates — use locators
  • Don't share app state across tests — isolation is critical
  • Don't run mobile tests on a Wi-Fi connection in CI — use wired
  • Don't test against production builds — use dedicated test builds
  • Don't skip testing on tablets — your responsive design may break

For more on test automation best practices, see our test automation strategy guide.

16. Author Bio & Next Steps

About the author: The SoftwareTestPilot Editorial Team is a group of QA practitioners with 40+ years of combined experience. We've shipped mobile apps at scale across banking, retail, and healthcare — and we test them in production every day.

🚀 Ready to Ship Mobile Faster? Try SoftwareTestPilot Pro

You just read 4,500+ words on Appium. Imagine having ready-to-run Appium starter kits for Android and iOS, parallel cloud execution configured, Page Object Model templates for the most common screens, and CI/CD pipeline snippets for GitHub Actions and GitLab CI.

SoftwareTestPilot Pro is the QA platform built for mobile-first teams:

  • ✅ Appium 2.0 starter kits for Android (UiAutomator2 + Espresso) and iOS (XCUITest)
  • ✅ POM templates for login, signup, checkout, search, and 20 other mobile screens
  • ✅ Cloud execution configs for BrowserStack, Sauce Labs, LambdaTest
  • ✅ CI/CD pipeline snippets for GitHub Actions, GitLab CI, Jenkins, CircleCI
  • ✅ Mobile testing strategy templates and device-matrix spreadsheets
  • ✅ AI-assisted test case generation trained on 100k+ real user stories

Start your 14-day free trial → SoftwareTestPilot Pro

No credit card. Cancel anytime. Made by QA engineers, for QA engineers.

Continue Your Mobile Testing Journey

Frequently asked questions

What is Appium?

Appium is an open-source mobile test automation framework based on the W3C WebDriver protocol. It supports native, hybrid, and mobile-web apps on Android and iOS, with bindings in Java, Python, JavaScript, C#, and Ruby.

Is Appium still relevant in 2026?

Yes. Appium 2.0 is the standard cross-platform mobile automation framework. It integrates cleanly with cloud device farms and is the most widely deployed mobile test tool on the planet.

What languages does Appium support?

Java, Python, JavaScript, C#, and Ruby — same W3C WebDriver API across all of them.

Can Appium test native iOS apps?

Yes, via the XCUITest driver. Note that iOS testing requires a macOS host because Apple's toolchain is macOS-only.

Can Appium test native Android apps?

Yes, via the UiAutomator2 or Espresso driver. UiAutomator2 is more flexible (cross-app); Espresso is faster (in-app only).

What's the difference between Appium and Selenium?

Appium extends the same W3C WebDriver protocol to mobile platforms. The patterns, locators, and POM are the same. The main difference is the driver: UiAutomator2 for Android, XCUITest for iOS.

How long does it take to set up Appium?

For an experienced QA engineer with Java/Python + Android Studio installed: 2–4 hours. For a beginner: 1–2 days.

Where can I practice?

Use the ApiDemos app (Android) or the Appium iOS Test App (iOS).

Is Appium slower than native frameworks?

Yes — Espresso and XCUITest are faster because they avoid the WebDriver bridge. Use them for performance-critical smoke tests; use Appium for cross-platform and cross-app scenarios.

How much does mobile cloud testing cost?

BrowserStack, Sauce Labs, and LambdaTest all start around $30–$150/month for small teams. Enterprise pricing scales with parallel sessions.

Keep going

Practice these questions

Rehearse Selenium and Playwright automation questions covering framework design, waits, locators and CI/CD.

Found this useful?
Share:XLinkedInWhatsApp

Was this article helpful?

Keep building your QA edge

Continue reading

Join the QA Community

Connect with fellow testers, share job leads, and get career advice.

Premium QA Resources

Stop Reinventing the Wheel. Upgrade Your QA Arsenal.

Take your testing skills from beginner to Lead Engineer. Supercharge your daily workflow with our premium digital resources.

  • ⚡ Ready-to-use testing strategy templates
  • 🔥 Advanced API & UI automation guides
  • ⏱️ Save 10+ hours a week on test planning
4.9/5 rating
Explore All Products

⭐⭐⭐⭐⭐ Trusted by 1,000+ Software Test Pilots • Instant Access