PRACTICAL GUIDE / Appium tutorial for beginners
Appium Tutorial for Beginners: Mobile Automation
Appium tutorial for beginners covering setup, capabilities, locators, waits, Android, iOS, real devices, permissions, examples, and pitfalls.
In this guide9 sections
- Understand the Appium Connection
- Prepare One Android Environment
- Create a Session With Explicit Options
- Write the First Test as a Complete Story
- Choose Locators That Can Survive a Redesign
- Handle Mobile State Deliberately
- Share Intent Across Android and iOS
- Debug From Evidence, Not Guesswork
- Your Next Appium Milestone
What you will learn
- Understand the Appium Connection
- Prepare One Android Environment
- Create a Session With Explicit Options
- Write the First Test as a Complete Story
The first Appium session is often harder than the first Appium test. A device must be visible, a platform driver must be installed, the app must be launchable, and several names must agree across the client, server, and operating system. When setup is vague, beginners spend hours changing code that was never the problem.
Start with one Android emulator and one short flow. Make that session repeatable before adding iOS, cloud devices, page objects, or parallel execution. The aim of this tutorial is a dependable baseline you can explain in an interview and rebuild on a new machine.
Understand the Appium Connection
Your test code does not directly tap the phone. The client sends WebDriver commands to the Appium server. Appium routes them through a platform driver, such as UiAutomator2 for Android or XCUITest for iOS. That driver communicates with the device and returns element or session data.
This separation matters during debugging. If the server cannot create a session, the test method has not started. If the session exists but a control cannot be found, inspect the app state and locator. If a tap reaches the wrong place, investigate element bounds, overlays, or a platform-specific gesture.
| Failure stage | Typical evidence | Check first |
|---|---|---|
| Client to server | Connection refused | Appium URL and running process |
| Session creation | Capability or driver error | Installed driver, platform, app path |
| App launch | Package or signing error | Build, bundle/package ID, permissions |
| Element lookup | No such element | Current screen, locator, wait |
| Interaction | Element not interactable | Visibility, keyboard, overlay, enabled state |
Appium is not a test runner. Use pytest, JUnit, TestNG, Mocha, or another runner to organize assertions, setup, and reports.
Prepare One Android Environment
Install a supported Node.js runtime, Appium, and the Android platform driver. Then verify the installation rather than assuming it worked:
npm install --global appium
appium driver install uiautomator2
appium driver list --installed
adb devices
appiumadb devices should show one emulator or authorized device. Multiple devices are fine later, but they require an explicit device identifier. For a first run, ambiguity adds no value.
Create a Python environment and install the client and test runner:
python -m venv .venv
source .venv/bin/activate
python -m pip install Appium-Python-Client pytestKeep the APK path, device name, and server URL in configuration rather than scattering them through tests. Do not commit a developer's absolute home-directory path as a team default.
Create a Session With Explicit Options
Modern Appium clients provide platform option classes. They catch some capability mistakes earlier and make intent clearer than an unstructured dictionary.
from pathlib import Path
from appium import webdriver
from appium.options.android import UiAutomator2Options
def create_driver():
options = UiAutomator2Options()
options.platform_name = "Android"
options.automation_name = "UiAutomator2"
options.device_name = "Pixel_API_35"
options.app = str(Path("apps/qabattle.apk").resolve())
options.no_reset = False
return webdriver.Remote(
"http://127.0.0.1:4723",
options=options,
)deviceName is required by the protocol but does not always select a particular Android device. When more than one device is connected, set udid to the value reported by adb devices. noReset=False asks for a clean application state; it does not erase every external server record.
For an already installed build, use appPackage and appActivity instead of an APK path. Choose one launch strategy and document it. A test that sometimes installs and sometimes reuses an app can produce confusing differences in permissions and stored data.
Write the First Test as a Complete Story
The example below verifies that empty login submission shows validation. Replace the accessibility IDs and expected message with identifiers from your app.
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
def test_empty_login_shows_validation():
driver = create_driver()
wait = WebDriverWait(driver, 10)
try:
sign_in = wait.until(EC.element_to_be_clickable(
(AppiumBy.ACCESSIBILITY_ID, "login.submit")
))
sign_in.click()
error = wait.until(EC.visibility_of_element_located(
(AppiumBy.ACCESSIBILITY_ID, "login.email.error")
))
assert error.text == "Email is required"
finally:
driver.quit()The finally block is not decoration. Without quit(), abandoned sessions consume emulator and server resources, especially after an assertion fails. In a real pytest suite, move driver creation and cleanup into a fixture so every test receives the same lifecycle guarantee.
Notice that the test does not pause for a fixed number of seconds. It waits for a condition that represents readiness. Appium commands already wait up to an implicit timeout when configured, but mixing large implicit waits with explicit waits can make timing hard to predict. A small or zero implicit wait plus focused explicit waits is easier to diagnose.
Choose Locators That Can Survive a Redesign
Ask developers to expose accessibility identifiers for meaningful controls. They are readable, can be shared across some Android and iOS implementations, and encourage an accessible product. The same identifier should describe the control's purpose, not its position.
Use this preference order as a review aid, not an absolute law:
- Accessibility ID for stable, interactive product elements.
- Android resource ID or iOS predicate/class chain for platform-specific screens.
- Visible text when the text itself is part of the behavior.
- Short CSS selectors for WebView content after switching context.
- XPath only when the app exposes no better contract.
An XPath copied from an inspector often encodes the complete hierarchy. Adding a wrapper view can break it even though the user experience is unchanged. If many tests need such XPath, the long-term fix is usually testability in the app, not a smarter XPath generator.
Handle Mobile State Deliberately
Mobile flows carry state that browser tests often avoid: permissions, keyboards, backgrounding, orientation, and app installation history. State must be part of the scenario.
For a permissions test, start from a known permission state and assert both choices. For an ordinary smoke test, pre-grant expected permissions through capabilities or setup so an unrelated system dialog does not block login. Do not write a universal helper that accepts every popup. It may approve a permission the product should never request.
When the keyboard covers a button, first ask whether the app should scroll or resize for a real user. Hiding the keyboard in the test may bypass a product defect. If dismissal is normal behavior, use the driver's keyboard command or tap a stable app element rather than hard-coded screen coordinates.
Network and account data need similar ownership. Seed records through APIs when possible. Use unique users for parallel tests. If a test changes a password or subscription state, its cleanup and collision strategy should be clear.
Share Intent Across Android and iOS
One codebase does not mean one identical implementation. A login journey can share test data and business assertions while keeping platform mechanics separate.
class LoginScreen:
def __init__(self, driver, platform):
self.driver = driver
self.email = (
AppiumBy.ACCESSIBILITY_ID,
"login.email" if platform == "Android" else "email-field",
)
def enter_email(self, value):
self.driver.find_element(*self.email).send_keys(value)This small distinction is honest. Android back navigation, iOS alerts, date pickers, scrolling, and permission dialogs do not become the same because a framework supports both. Avoid conditionals spread across every test. Put unavoidable platform details behind focused screen or service objects, then keep the scenario readable.
Use simulators and emulators for fast pull-request feedback. Add a risk-based real-device set for gestures, OEM Android behavior, camera, biometrics, notifications, performance, and other hardware-dependent behavior. A large random device grid is expensive and still may miss the device your users actually have.
Debug From Evidence, Not Guesswork
When a test fails, collect the server log, device log, screenshot, page source, platform version, app build, and session capabilities. Those artifacts answer different questions. A screenshot shows visible state; page source shows what the automation driver can locate; server logs show command routing and timeouts.
Common beginner mistakes include:
- Starting a new driver inside every helper, creating unrelated sessions.
- Using
time.sleep()to cover uncertain loading. - Keeping
noReset=Trueand depending on a manually logged-in device. - Copying absolute XPath locators from the inspector.
- Treating a system permission dialog as an application element.
- Calling
quit()only after passing assertions. - Building page-object infrastructure before one test can run reliably in CI.
If the app is on the expected screen but the element is absent from page source, inspect its accessibility exposure with the development team. Repeating the lookup cannot discover a control the platform driver cannot see.
Your Next Appium Milestone
Make the baseline reproducible: one documented emulator, one installed Appium platform driver, one app build, and one pytest command that creates and closes a session. Then add three checks: application launch, one critical happy path, and one meaningful error path. Capture artifacts automatically on failure.
Only after those checks pass repeatedly in local and CI runs should you add page objects, a second platform, real-device providers, or parallel sessions. Appium scales best when each new layer solves a failure you have observed, not one you imagine a future framework might have.
// FIELD DISPATCH
Get the QA Field Notes
Weekly QA battles, AI testing guides, and interview drills. Free on Substack.
// LIVE COURSE / THE TESTING ACADEMY
Playwright Automation Mastery
Go beyond Selenium. Master Playwright with JS/TS in 90 days.
From the instructor behind this guide.
Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.
PRIMARY REFERENCES
Verify the details at the source
QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.
- 01
- 02
FAQ / QUICK ANSWERS
Questions testers ask
What is Appium?
Appium is an open source automation framework for testing mobile apps across Android, iOS, hybrid, and mobile web contexts. It drives apps through platform automation engines such as UiAutomator2 for Android and XCUITest for iOS.
Is Appium good for beginners?
Appium is approachable if you already understand basic automation and mobile testing concepts. Beginners should start with one platform, one device, stable accessibility ids, and a short smoke flow before building a large framework.
Do I need real devices for Appium?
Use both when possible. Emulators and simulators are useful for fast feedback, but real devices expose performance, permissions, gestures, keyboards, sensors, notifications, and vendor differences that virtual devices can miss.
What locators should I use in Appium?
Prefer accessibility id where the app supports it. It is usually more stable and helps accessibility. Use platform specific locators carefully, and avoid brittle XPath based on hierarchy unless there is no better option.
Can Appium test both Android and iOS with the same code?
Some flows and abstractions can be shared, but Android and iOS have platform differences in locators, permissions, gestures, system dialogs, and navigation. Share business intent, but keep platform details explicit where needed.
RELATED GUIDES
Continue the learning route
GUIDE 01
Mobile App Testing Guide: Strategy and Checklist
Mobile app testing guide with strategy, device matrix, functional cases, usability, performance, security, automation, release checks, and QA tips.
GUIDE 02
Test Cases for Mobile App: Complete QA Checklist
Test cases for mobile app projects covering install, login, permissions, network changes, gestures, notifications, performance, security, and upgrades.
GUIDE 03
Android Testing with Espresso: Practical Guide
Android testing with Espresso guide for UI tests, matchers, actions, assertions, idling resources, test data, architecture, and common mistakes.
GUIDE 04
iOS Testing with XCUITest: Practical Guide
iOS testing with XCUITest guide for UI automation, identifiers, assertions, waits, simulators, real devices, launch state, and common mistakes.