World’s largest virtual agentic engineering & quality conference

WHENAUG 19-21
WHEREVirtual · Global
Register Now
Automation

Appium Tutorial: A Detailed Guide To Appium Testing

Appium tutorial for Appium 3: install the server and drivers, write a runnable first test, fix common errors, and run tests on real Android and iOS devices.

Author

Tanay Kumar Deo

Author

Author

Srinivasan Sekar

Reviewer

Last Updated on: August 5, 2026

Appium automates a signed application binary on Android and iOS through one W3C WebDriver API, with no changes to the application source. That single property is why it survived a decade of mobile tooling churn.

This tutorial covers Appium 3, released in 2026. By the end you will have the server and a driver installed, plus a first test that runs against a real element.

It also leaves you with a lookup table for the errors that break most Appium setups.

Overview

Is Appium Still Worth Learning in 2026?

Yes, for teams shipping one app to both platforms. No other mature framework drives a production binary on Android and iOS through a single API without touching application code.

What Do You Need Before Running Your First Appium Test?

Five pieces have to be in place before a session will start:

  • Node.js 20.19 or newer: Appium 3 also accepts 22.12 and above, or any 24 release.
  • A platform driver: UiAutomator2 for Android, XCUITest for iOS, installed separately.
  • A client library: the Appium package for Java, Python, JavaScript, Ruby, or C#.
  • A target device: a physical handset over USB, an emulator, or a simulator.
  • A signed app binary: an .apk or .aab for Android, an .ipa or .app for iOS.

What Is Appium?

Appium is an open source automation framework that drives native, hybrid, and mobile web apps on Android, iOS, and Windows through one W3C WebDriver API and platform specific drivers.

Dan Cuellar built the first version at Zoosk in 2011, and Jason Huggins put it behind the WebDriver wire protocol a year later. That decision is why Appium tests read like Selenium tests today.

The framework is a Node.js HTTP server. Your test sends WebDriver commands over HTTP, the server routes them to a driver, and the driver talks to the platform automation tool on the device.

With Appium you can automate three application types from the same suite:

  • Native apps, installed on the device and built with the platform SDKs.
  • Mobile web apps, opened in Chrome or Safari on the handset itself.
  • Hybrid apps, where native shells wrap a webview rendering web content.

Hybrid apps are the ones that trip teams up. Appium exposes native and webview contexts separately, so a test has to switch context explicitly before webview elements become visible.

The split matters when you choose a locator strategy. For a breakdown of the three build models, see Web vs Hybrid vs Native Apps.

This short video walks through the same model visually.

How Does the Appium Architecture Work?

Appium architecture has three parts: a client library in your language, an HTTP server that speaks W3C WebDriver, and a platform driver such as UiAutomator2 or XCUITest on the device.

A session begins when the client posts a capabilities object to the server. The server reads the capability values, picks a matching driver, and returns a session ID that scopes every later command.

Appium architecture showing client, server, and driver layers

Each driver wraps the automation tool that Apple or Google already ships. The driver layer is where the platform difference lives, and it is the only part of the stack that changes per platform.

LayerAndroidiOS
DriverUiAutomator2XCUITest
On device agentUiAutomator2 server APKWebDriverAgent
Underlying frameworkAndroid UiAutomatorApple XCTest
Transport to deviceadb port forwardingUSB tunnel on a local port
Host requirementAny OS with Android SDKmacOS with Xcode
First session costSeconds to install the agentLonger, WebDriverAgent compiles

That last row explains a symptom most teams hit early. The first time I saw it I assumed the handset was faulty, but WebDriverAgent was simply compiling and signing before any command could run.

A deeper component walkthrough lives in our guide to Appium architecture.

What Changed Between Appium 1, Appium 2, and Appium 3?

Appium 1 bundled every driver in the core. Appium 2 split drivers and plugins into installable packages. Appium 3 requires Node 20.19, drops deprecated endpoints, and ships Inspector as a plugin.

Most Appium content still on the web describes version 1, and that is the single biggest source of broken setups. The table below shows what actually differs across the three releases.

AreaAppium 1Appium 2Appium 3
DriversBundled in coreInstalled via CLIInstalled via CLI
ProtocolJSON Wire and W3CW3C onlyW3C only
Node.js8 and above14 and above20.19 and above
Server base path/wd/hubRoot, /wd/hub optionalRoot
CapabilitiesFlat keysVendor prefixedVendor prefixed
GesturesTouchActionW3C ActionsW3C Actions
PluginsNot supportedIntroducedInspector ships as one
Deprecated endpointsPresentSupportedRemoved or driver owned
Session discovery/sessions/sessions/appium/sessions behind a flag
Server frameworkExpress 4Express 4Express 5

Three of those rows break old scripts silently rather than loudly. Unprefixed capabilities are rejected, TouchAction imports fail to resolve, and a URL ending in wd/hub returns a 404 on Appium 3.

A full breakdown of the newest release is in our guide to Appium 3 features. For upgrading an existing suite, follow the Appium 2 migration guide first, then apply the Node and endpoint changes above.

Note

Note: Appium 3.6.0 shipped in July 2026. Release notes for every version live on the Appium releases page on GitHub.

How Do You Install and Configure Appium?

Install Appium 3 with npm install -g appium, add a platform driver using appium driver install uiautomator2 or xcuitest, then confirm the setup with appium driver doctor before running tests.

Prerequisites are light. You need the Node.js runtime at version 20.19 or newer, npm 10 or newer, and the platform SDK for the operating system you automate.

Install the Appium Server

Install the server globally from npm:

npm install -g appium
Installing Appium globally from the command line with npm

Confirm the version before going further:

appium --version
Verifying the installed Appium version in the terminal

Expected output: a version string such as 3.6.0. A command not found response usually means npm installed to a directory outside your PATH.

Expected output of the appium version command

Note: Appium Desktop is no longer part of this workflow. That project was archived in 2023, is incompatible with Appium 2 and later, and carries an unpatched remote code execution advisory.

Install a Platform Driver

Since Appium 2, drivers ship separately from the server. A fresh install automates nothing until you add at least one driver.

# See what is available for your platform
appium driver list

# Android
appium driver install uiautomator2

# iOS, macOS hosts only
appium driver install xcuitest

# Confirm what is installed
appium driver list --installed

# Update a driver in place
appium driver update uiautomator2

Verify the Toolchain

Each driver ships its own environment check. Run it before writing a test, because it catches missing SDK paths and signing problems that otherwise surface as confusing session errors.

appium driver doctor uiautomator2

This replaces the standalone appium-doctor package, which is no longer maintained. The background on what those checks cover is in our guide to Appium Doctor.

Install Plugins

Plugins extend the server without forking it. Appium 3 ships Inspector as a plugin, so this is how most teams now get element inspection.

appium plugin list
appium plugin install inspector
appium plugin list --installed
Listing and installing Appium plugins from the command line

Platform specific setup steps are covered in our walkthrough on how to install Appium.

Run iOS + Android tests written by your AI agent.

Appium

How Do You Use Appium Inspector to Find Elements?

Appium Inspector renders the live element tree of a running app, so you can read the exact attributes a locator needs and see why a selector matched nothing before editing the test.

Three ways to run it exist today, and they behave identically once a session starts:

  • As the Appium 3 server plugin, installed with appium plugin install inspector.
  • As a desktop app from the Appium Inspector releases page for macOS, Linux, and Windows.
  • As a hosted web app, which needs no local install but requires a reachable server.

Point it at your server host and port, paste the same capabilities your test will use, then start a session. The app installs on the target device and the element tree loads.

Appium Inspector session configuration with capabilities

Inspector offers both an accessibility identifier and a generated XPath for the same node. I copy the identifier every time, because the XPath breaks on the next layout change.

More detail on the workflow is in our guide to Appium Inspector for apps.

How Do You Write Your First Appium Test?

Writing an Appium test takes four steps: install the language client, define W3C capabilities in a driver options object, point the client at the running server, then locate elements and assert.

The example below automates an Android app in Python. Before starting, make sure Python 3.9 or newer is installed and a device or emulator is connected.

Step 1: Install the Client Library

pip install Appium-Python-Client
Installing the Appium Python client library with pip

Step 2: Connect a Device

For a physical handset, enable USB debugging under developer options, then confirm the device is visible:

adb devices

Emulators need no cable. Launch one from the device manager in Android Studio instead.

Launching an Android emulator from the Android Studio device manager

Step 3: Start the Server

appium --port 4723

Expected output: a line reading that the Appium REST http interface listener started on 127.0.0.1:4723. Leave this terminal open while tests run.

Step 4: Write the Test

Capabilities are key value pairs that tell the server which driver, device, and binary to use. Every non standard key needs the appium vendor prefix from version 2 onward.

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

capabilities = {
    "platformName": "Android",
    "appium:platformVersion": "14",
    "appium:deviceName": "Pixel 8",
    "appium:app": "/apk/com.slot.spin.game.play.apk",
    "appium:appPackage": "com.slot.spin.game.play",
    "appium:appActivity": ".MainActivity",
    "appium:automationName": "UiAutomator2",
}

options = UiAutomator2Options().load_capabilities(capabilities)

# Appium 2 and 3 serve the W3C endpoint at the root. A trailing
# /wd/hub returns 404 on these versions.
driver = webdriver.Remote("http://127.0.0.1:4723", options=options)

try:
    # An explicit wait returns the moment the element is ready,
    # so the test stays fast on quick devices and stable on slow ones.
    spin_button = WebDriverWait(driver, 20).until(
        EC.element_to_be_clickable(
            (AppiumBy.ID, "com.slot.spin.game.play:id/playBtn")
        )
    )
    spin_button.click()

    result = WebDriverWait(driver, 20).until(
        EC.presence_of_element_located(
            (AppiumBy.ID, "com.slot.spin.game.play:id/resultText")
        )
    )
    print("Spin result:", result.text)
finally:
    driver.quit()

Expected output: the app launches on the device, the spin button is tapped, and the terminal prints a line beginning with Spin result followed by the on screen value.

Running the first Appium test script against an Android app

Java teams use the same capability keys through a typed options object. The client differs, the protocol underneath does not.

UiAutomator2Options options = new UiAutomator2Options()
        .setPlatformVersion("14")
        .setDeviceName("Pixel 8")
        .setApp("/apk/com.slot.spin.game.play.apk")
        .setAppPackage("com.slot.spin.game.play")
        .setAppActivity(".MainActivity");

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

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
wait.until(ExpectedConditions.elementToBeClickable(
        AppiumBy.id("com.slot.spin.game.play:id/playBtn"))).click();

driver.quit();

Building capability sets by hand gets tedious across a device matrix. Our capabilities generator produces valid W3C blocks for either client.

Note

Note: Ready to run this suite against physical hardware? Start free on 10,000+ real devices.

Which Locator Strategy Should You Use in Appium?

Prefer accessibility id in Appium because it maps to content-desc on Android and the accessibility identifier on iOS. Fall back to resource id, then class chain, and avoid XPath in hot paths.

Locator choice is the single largest cause of flaky mobile suites, and nearly every unstable suite I have inherited had XPath at its core. Those queries force the driver to serialise the whole element tree.

PriorityStrategyWorks onWhy
1Accessibility IDBothOne locator for both platforms, fastest lookup
2ID or resource idAndroidDirect index lookup, no tree walk
3iOS class chainiOSNative XCUITest query, far faster than XPath
4Android UiAutomatorAndroidRuns on device, supports scrolling into view
5XPathBothLast resort, brittle and slow on large trees

Getting to priority one is a development task, not a QA task. Someone has to set content-desc on Android views and accessibilityIdentifier on iOS views, ideally to the same string.

When that groundwork exists, one locator serves both platforms:

# Same locator, both platforms, no branching
driver.find_element(AppiumBy.ACCESSIBILITY_ID, "checkout_button")

# Android only, still fast
driver.find_element(AppiumBy.ID, "com.shop.app:id/checkout")

# iOS class chain, prefer over XPath
driver.find_element(
    AppiumBy.IOS_CLASS_CHAIN,
    '**/XCUIElementTypeButton[`label == "Checkout"`]'
)

A common failure looks like a locator problem but is not. If an element is present in Inspector yet invisible to the test, check whether the app switched into a webview context.

Our guide to locators in Appium holds a wider catalogue of selectors and the syntax each one expects.

How Do You Handle Waits in Appium?

Use WebDriverWait with expected conditions instead of fixed sleeps. An explicit wait returns as soon as the element is ready, so tests stay fast on quick devices and stable on slow ones.

Fixed sleeps fail in both directions. Too short and the suite breaks on a cold device. Too long and every run pays the worst case, which compounds across hundreds of steps.

Wait typeScopeUse it when
Implicit waitEvery find call in the sessionRarely, as a low safety net only
Explicit waitOne condition at one pointDefault choice for element readiness
Fluent waitOne condition, custom pollingSlow renders needing tuned intervals
Fixed sleepBlocks the whole threadAlmost never, debugging aside

Mixing implicit and explicit waits is the trap. I lost most of an afternoon to a five second wait that blocked for thirty, because a leftover implicit wait was compounding with it.

# Pick one strategy. If you use explicit waits, leave
# the implicit wait at zero.
driver.implicitly_wait(0)

wait = WebDriverWait(driver, 20)

# Present in the tree, may still be off screen
wait.until(EC.presence_of_element_located(
    (AppiumBy.ACCESSIBILITY_ID, "cart_badge")))

# Rendered and tappable, the condition most steps want
wait.until(EC.element_to_be_clickable(
    (AppiumBy.ACCESSIBILITY_ID, "checkout_button"))).click()

# Gone, useful after dismissing a loader
wait.until(EC.invisibility_of_element_located(
    (AppiumBy.ACCESSIBILITY_ID, "loading_spinner")))

Animations cause a subtle version of this. An element can be clickable while still sliding into place, so the tap lands on the wrong coordinates and the step fails intermittently.

Waiting on a post animation condition, such as a settled label, is more reliable than waiting on the element itself. Gesture level timing is covered in Appium gestures.

How Do You Run Appium Tests on Real Devices at Scale?

Point the same test at a device cloud instead of localhost. Only the remote URL and a few vendor prefixed capabilities change, so one suite fans out across many real Android and iOS handsets.

A local setup proves the test works. It does not prove the app works across the device and OS spread your users actually run, and maintaining that hardware is a full time job.

TestMu AI is an AI native test orchestration and execution platform whose real device cloud runs Appium suites on 10,000+ Android and iOS handsets.

Your test logic stays untouched. Everything below the assertions is the same suite you wrote above, now pointed somewhere else.

  • Real device grid: run against shipped hardware, not emulators, on demand.
  • Parallel execution: fan one suite across many device and OS combinations at once.
  • Device logs and video: every session records logs and a replay for triage.
  • Network simulation: reproduce throttled and offline conditions that break real users.
  • Geolocation testing: validate location gated flows without physically travelling.

Setup steps and capability reference live in the getting started with Appium testing documentation.

Only two things change in the script. The remote URL points at the cloud hub, and every vendor setting moves into an lt:options block beside the standard capabilities.

from appium import webdriver
from appium.options.android import UiAutomator2Options

username = "YOUR_USERNAME"
access_key = "YOUR_ACCESS_KEY"

capabilities = {
    "platformName": "Android",
    "appium:platformVersion": "14",
    "appium:deviceName": "Pixel 8",
    "appium:app": "lt://APP_ID",
    "appium:automationName": "UiAutomator2",
    # Cloud vendor settings live in lt:options, not the appium namespace
    "lt:options": {
        "username": username,
        "accessKey": access_key,
        "build": "Slot Machine Regression",
        "name": "Spin flow, Pixel 8",
        "isRealMobile": True,
        "network": True,
        "video": True,
        "deviceLog": True,
    },
}

options = UiAutomator2Options().load_capabilities(capabilities)

remote_url = f"https://{username}:{access_key}@mobile-hub.lambdatest.com/wd/hub"
driver = webdriver.Remote(remote_url, options=options)

# The rest of the test is identical to the local version.

One difference is worth planning for. The app capability takes an lt:// identifier returned by the upload API, not a local file path.

That means a build upload step belongs in the pipeline before test execution starts.

Fanning the same suite across devices is where the time is recovered. Our guide to Appium Parallel Testing covers session limits and result aggregation.

Test your website on the TestMu AI real device cloud

How Do You Fix Common Appium Errors?

Most Appium errors trace to environment drift rather than test logic. Match the message to its cause below, then rerun the driver doctor check before editing any locators or waits.

The table maps each message practitioners actually see to the cause behind it.

ErrorUsual causeFix
404 on session creationURL still ends in wd/hubPoint the client at the server root
Invalid or unknown capabilityMissing appium vendor prefixPrefix every non standard key
No driver found for automationNameDriver never installedRun appium driver install for the platform
Device not found by adbEmulator booted from a stale snapshotCold boot it from the AVD manager
WebDriverAgent fails to buildSigning or provisioning mismatchSet a development team, unlock the keychain
xcodebuild exited with code 65Device symbols not downloadedOpen Xcode devices window, let it finish
Element not found, visible in InspectorSession sitting in the wrong contextSwitch to the webview context first
Session ends mid runnewCommandTimeout elapsedRaise the timeout or remove long sleeps
TouchAction import failsClass removed in current clientsRewrite the gesture using W3C Actions

Two of those rows deserve emphasis because they mimic real bugs. A session that dies mid run looks like an app crash, and a context mismatch looks like a missing element.

Now I rerun the driver doctor check before reading a single stack trace. Toolchain drift after an Xcode or Android SDK upgrade explains more failures than any change your team made to the test cases.

Server logs are the other underused signal. Running the server with the debug log level prints the exact capability payload it received, which settles most argument mismatches in seconds.

When Should You Not Use Appium?

Skip Appium when you need frame accurate gaming input, deep OS level control, or sub second unit feedback. Espresso and XCUITest run in process and give faster, more stable results there.

The cost of the cross platform abstraction is real. Every command crosses HTTP and a driver boundary, which adds latency and one more layer that can fail independently of your app.

FrameworkPlatformsRunsBest for
AppiumAndroid, iOS, WindowsOut of processOne suite across both platforms
EspressoAndroid onlyIn processFast Android feedback inside CI
XCUITestiOS onlyIn processNative iOS suites owned by developers
DetoxAndroid, iOSGrey boxReact Native apps needing auto sync
MaestroAndroid, iOSOut of processQuick YAML flows over deep assertions

Four constraints show up often enough to plan around:

  • iOS automation requires a macOS host, which constrains CI runner choice.
  • Flutter apps render to a canvas, so standard drivers see no element tree.
  • Reporting is minimal, so test reports come from your runner.
  • Every iOS CI job pays the WebDriverAgent build again, which inflates pipeline time.

A pragmatic split works well in practice. Developers keep fast in process tests per platform, and QA owns one Appium suite covering the cross platform journeys that matter to release decisions.

Full evaluations of each option are in our roundup of Appium alternatives.

Key Takeaways

Appium earns its place when one app ships to two platforms and you refuse to modify the binary to test it. That constraint, not raw speed, is what the framework optimises for.

Three decisions determine whether a suite stays healthy. Install drivers explicitly, standardise on accessibility identifiers, and replace every fixed sleep with an explicit wait before the suite grows.

Next step: run the test above on a real handset, then keep the Appium commands cheat sheet open while you extend it. Teams formalising skills can take the Appium 101 certification.

Verified against Appium 3.6.0 and the official Appium documentation in August 2026.

Author

...

Tanay Kumar Deo

Blogs: 16

  • Twitter
  • Linkedin

Tanay kumar deo is a skilled software developer with expertise in Android and web development, he is always eager to expand his skill set and take on new challenges. Whether developing software or sharing his knowledge with others, he is driven by a desire to make a positive impact on the world around him. In addition to his technical abilities, Tanay also possesses excellent blogging and writing skills, which allow him to effectively communicate his ideas and insights to a wider audience.

Reviewer

...

Srinivasan Sekar

Reviewer

  • Linkedin

Srinivasan Sekar is Director of Engineering at TestMu AI (formerly LambdaTest), where he leads engineering and open-source initiatives behind the Selenium and Appium automation grid and owns TestMu AI's MCP Server. A committer to Appium and a contributor to Selenium, WebdriverIO, Taiko, and AppiumTestDistribution, he brings over 15 years of experience in quality engineering and open-source technologies. He is the author of the Apress book 'The MCP Standard: A Developer's Guide to Building Universal AI Tools with the Model Context Protocol,' a Certified Kubernetes and Cloud Native Associate, and an international conference speaker. Before TestMu AI he spent over eight years at Thoughtworks as a Principal Consultant and Quality Architect. Srinivasan holds a B.Tech in Information Technology from Anna University.

Open in ChatGPT Icon

Open in ChatGPT

Open in Claude Icon

Open in Claude

Open in Perplexity Icon

Open in Perplexity

Open in Grok Icon

Open in Grok

Open in Gemini AI Icon

Open in Gemini AI

Copied to Clipboard!
...

3000+ Browsers. One Platform.

See exactly how your site performs everywhere.

Try it free
...

Write Tests in Plain English with KaneAI

Create, debug, and evolve tests using natural language.

Try for free
...
TestMu Conf 2026

World's largest virtual agentic engineering & quality conference

...

AUG 19-21, 2026

REGISTER NOW

Appium FAQs

Did you find this page helpful?

More Related Blogs

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests