Appium Cheat Sheet
Overview
Appium is an open-source test automation framework for native, hybrid, and mobile web applications. It uses the WebDriver protocol (W3C standard) to drive iOS, Android, and Windows apps, allowing testers to write tests in any language that has a WebDriver client library — including Java, Python, JavaScript, Ruby, C#, and more. Appium follows a philosophy of not requiring app modifications for testing; it tests the same app binary that goes to production.
Appium 2.x introduces a driver/plugin architecture where platform support is installed as separate drivers (XCUITest for iOS, UiAutomator2 for Android, Windows for desktop). It supports real devices and emulators/simulators, provides element inspection tools, handles gestures and multi-touch interactions, and integrates with CI/CD pipelines. Appium can also test Flutter apps, React Native apps, and Espresso-based tests through specialized drivers.
Installation
# Prerequisites: Node.js 16+ and npm
node --version
npm --version
# Install Appium 2.x
npm install -g appium
# Install drivers
appium driver install uiautomator2 # Android
appium driver install xcuitest # iOS
appium driver install mac2 # macOS
appium driver install windows # Windows
# Verify installation
appium --version
appium driver list --installed
# Install Appium Doctor (checks prerequisites)
npm install -g @appium/doctor
appium-doctor --android
appium-doctor --ios
# Android prerequisites
# - Java JDK 11+
# - Android SDK (via Android Studio)
# - ANDROID_HOME environment variable
# - Platform tools in PATH
export ANDROID_HOME=$HOME/Android/Sdk
export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools/bin
# iOS prerequisites (macOS only)
# - Xcode with command line tools
# - Carthage: brew install carthage
xcode-select --install
brew install carthage
# Install Appium Inspector (GUI element inspector)
# Download from https://github.com/appium/appium-inspector/releases
# Start Appium server
appium
appium --port 4723 --log-level info
appium --allow-cors # For Appium Inspector
Desired Capabilities
# Android capabilities
android_caps = {
"platformName": "Android",
"appium:automationName": "UiAutomator2",
"appium:deviceName": "Pixel_7_API_34",
"appium:app": "/path/to/app.apk",
"appium:appPackage": "com.example.myapp",
"appium:appActivity": "com.example.myapp.MainActivity",
"appium:noReset": True,
"appium:fullReset": False,
"appium:newCommandTimeout": 300,
"appium:autoGrantPermissions": True,
}
# iOS capabilities
ios_caps = {
"platformName": "iOS",
"appium:automationName": "XCUITest",
"appium:deviceName": "iPhone 15 Pro",
"appium:platformVersion": "17.4",
"appium:app": "/path/to/app.ipa",
"appium:bundleId": "com.example.myapp",
"appium:noReset": True,
"appium:xcodeOrgId": "TEAM_ID",
"appium:xcodeSigningId": "iPhone Developer",
"appium:udid": "auto",
}
# Mobile web capabilities
web_caps = {
"platformName": "Android",
"appium:automationName": "UiAutomator2",
"appium:deviceName": "emulator-5554",
"browserName": "Chrome",
}
Python Client Setup
# Install client
# pip install Appium-Python-Client
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.options.ios import XCUITestOptions
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Connect to Appium server
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "emulator-5554"
options.app = "/path/to/app.apk"
driver = webdriver.Remote(
command_executor="http://127.0.0.1:4723",
options=options
)
# Basic operations
driver.find_element(AppiumBy.ID, "com.example:id/username").send_keys("testuser")
driver.find_element(AppiumBy.ID, "com.example:id/password").send_keys("pass123")
driver.find_element(AppiumBy.ID, "com.example:id/login_btn").click()
# Wait for element
wait = WebDriverWait(driver, 10)
element = wait.until(
EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "Welcome"))
)
# Cleanup
driver.quit()
Element Locator Strategies
| Strategy | Python Usage | Description |
|---|---|---|
| ID | AppiumBy.ID, "resource-id" | Android resource ID |
| Accessibility ID | AppiumBy.ACCESSIBILITY_ID, "label" | Content description / accessibility label |
| XPath | AppiumBy.XPATH, "//android.widget.Button" | XPath expression |
| Class Name | AppiumBy.CLASS_NAME, "android.widget.TextView" | UI class name |
| Android UIAutomator | AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector()...' | Android UIAutomator selector |
| iOS Class Chain | AppiumBy.IOS_CLASS_CHAIN, "**/XCUIElementTypeButton" | iOS class chain |
| iOS Predicate | AppiumBy.IOS_PREDICATE, "label == 'Login'" | iOS predicate string |
# Android UIAutomator selectors
driver.find_element(
AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().text("Login")'
)
driver.find_element(
AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().resourceId("com.example:id/btn").enabled(true)'
)
driver.find_element(
AppiumBy.ANDROID_UIAUTOMATOR,
'new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView('
'new UiSelector().text("Target Item"))'
)
# iOS predicate string
driver.find_element(
AppiumBy.IOS_PREDICATE,
'type == "XCUIElementTypeButton" AND label CONTAINS "Submit"'
)
# iOS class chain
driver.find_element(
AppiumBy.IOS_CLASS_CHAIN,
'**/XCUIElementTypeCell[`name CONTAINS "item"`]'
)
Gestures and Actions
from appium.webdriver.common.touch_action import TouchAction
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.actions import interaction
from selenium.webdriver.common.actions.pointer_input import PointerInput
# Tap
element = driver.find_element(AppiumBy.ID, "button")
element.click()
# Long press (W3C Actions)
actions = ActionChains(driver)
actions.click_and_hold(element).pause(2).release().perform()
# Swipe using W3C Actions
def swipe(driver, start_x, start_y, end_x, end_y, duration=800):
pointer = PointerInput(interaction.POINTER_TOUCH, "finger")
actions = ActionChains(driver)
actions.w3c_actions.devices.append(pointer)
actions.w3c_actions.pointer_action.move_to_location(start_x, start_y)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.pause(duration / 1000)
actions.w3c_actions.pointer_action.move_to_location(end_x, end_y)
actions.w3c_actions.pointer_action.pointer_up()
actions.perform()
# Swipe up
size = driver.get_window_size()
swipe(driver, size['width']//2, size['height']*3//4,
size['width']//2, size['height']//4)
# Scroll to element (Android)
driver.find_element(
AppiumBy.ANDROID_UIAUTOMATOR,
'new UiScrollable(new UiSelector().scrollable(true))'
'.scrollIntoView(new UiSelector().text("Target"))'
)
# Pinch and zoom (multi-touch)
# Use mobile: commands
driver.execute_script('mobile: pinchOpen', {
'elementId': element.id,
'percent': 0.75,
'speed': 2500
})
App Management
# App lifecycle
driver.install_app("/path/to/app.apk")
driver.remove_app("com.example.myapp")
driver.activate_app("com.example.myapp")
driver.terminate_app("com.example.myapp")
is_installed = driver.is_app_installed("com.example.myapp")
driver.background_app(5) # Background for 5 seconds
# Context switching (hybrid apps)
contexts = driver.contexts
print(contexts) # ['NATIVE_APP', 'WEBVIEW_com.example']
driver.switch_to.context('WEBVIEW_com.example')
# Now use web selectors
driver.find_element(AppiumBy.CSS_SELECTOR, "#login-form")
driver.switch_to.context('NATIVE_APP')
# Screenshots
driver.save_screenshot("screenshot.png")
element.screenshot("element.png")
# Device actions
driver.orientation = "LANDSCAPE"
driver.orientation = "PORTRAIT"
driver.open_notifications() # Android
driver.press_keycode(4) # Android back button
driver.hide_keyboard()
driver.lock()
driver.unlock()
Configuration
// .appiumrc.json (Appium server config)
{
"server": {
"port": 4723,
"host": "0.0.0.0",
"log-level": "info",
"log": "appium.log",
"allow-cors": true,
"keep-alive-timeout": 600
}
}
# pytest conftest.py
import pytest
from appium import webdriver
from appium.options.android import UiAutomator2Options
@pytest.fixture(scope="session")
def driver():
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "emulator-5554"
options.app = "path/to/app.apk"
options.no_reset = True
driver = webdriver.Remote("http://127.0.0.1:4723", options=options)
driver.implicitly_wait(10)
yield driver
driver.quit()
Advanced Usage
# Page Object pattern
class LoginPage:
def __init__(self, driver):
self.driver = driver
@property
def username_field(self):
return self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "username")
@property
def password_field(self):
return self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "password")
@property
def login_button(self):
return self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "loginButton")
def login(self, username, password):
self.username_field.send_keys(username)
self.password_field.send_keys(password)
self.login_button.click()
return HomePage(self.driver)
# Parallel execution with pytest-xdist
# conftest.py
def get_device(worker_id):
devices = {
"gw0": {"udid": "emulator-5554", "port": 4723},
"gw1": {"udid": "emulator-5556", "port": 4725},
}
return devices.get(worker_id, devices["gw0"])
# Run: pytest -n 2 tests/
Troubleshooting
| Issue | Solution |
|---|---|
| ”Could not find adb” | Set ANDROID_HOME; ensure platform-tools in PATH |
| Session creation fails | Check Appium server running; verify capabilities match device |
| Element not found | Use Appium Inspector to verify locator; add explicit wait |
| iOS real device fails | Configure code signing; set xcodeOrgId and xcodeSigningId |
| App not installing | Check APK/IPA path; verify device storage space |
| Timeout errors | Increase newCommandTimeout; use explicit waits |
| WebView context missing | Enable WebView debugging in app; check chromedriverExecutable |
| Emulator not starting | Start emulator manually first; check AVD configuration |
| Gestures not working | Use W3C Actions API; check coordinates are within screen bounds |
| Parallel tests conflict | Use different ports and device UDIDs for each session |