
Android Testing with Appium: Complete Guide to Android Automation
Android dominates the global mobile market, making Android testing essential for most mobile applications. Appium's UiAutomator2 driver leverages Google's native UI testing framework to provide powerful, reliable automation across the diverse Android ecosystem.
This guide covers everything specific to Android testing with Appium, from environment setup to advanced Android-only features and optimization techniques.
Table Of Contents-
Android Testing Prerequisites
Android automation works on Windows, macOS, and Linux:
Software Requirements:
- Java JDK 11 or later
- Android Studio (includes SDK)
- Android SDK Platform Tools
- Node.js 18+
- Appium 2.x with UiAutomator2 driver
Environment Variables:
# Add to your shell profile (.bashrc, .zshrc, etc.)
export ANDROID_HOME=$HOME/Android/Sdk # macOS/Linux
# Windows: Usually C:\Users\<user>\AppData\Local\Android\Sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/platform-tools
export PATH=$PATH:$ANDROID_HOME/tools/binVerify Setup:
# Check ADB
adb --version
# Check Java
java -version
# Check Android SDK
echo $ANDROID_HOMESetting Up the Environment
Install Android Studio
Download from developer.android.com (opens in a new tab) and install. During setup:
- Select "Standard" installation
- Install recommended SDK components
- Accept all licenses
Configure SDK Components
Open Android Studio → SDK Manager:
- SDK Platforms: Install target API levels (e.g., Android 14, API 34)
- SDK Tools: Ensure these are installed:
- Android SDK Build-Tools
- Android SDK Platform-Tools
- Android Emulator
- Intel HAXM (for faster emulation on Intel Macs/PCs)
Create an Emulator
- Open AVD Manager (Android Studio → Tools → Device Manager)
- Click "Create Device"
- Select a device definition (e.g., Pixel 6)
- Select a system image (e.g., API 34 with Google Play)
- Finish and launch
Install Appium and UiAutomator2
# Install Appium
npm install -g appium
# Install UiAutomator2 driver
appium driver install uiautomator2
# Verify installation
appium driver list --installedVerify Complete Setup
npm install -g @appium/doctor
appium-doctor --android
# All checks should passEmulator vs Real Device Testing
Emulator Testing
Emulators are convenient for development and CI:
const capabilities = {
platformName: 'Android',
'appium:automationName': 'UiAutomator2',
'appium:deviceName': 'Pixel_6_API_34',
'appium:app': '/path/to/app.apk',
};Emulator management with ADB:
# List running emulators/devices
adb devices
# Start emulator from command line
emulator -avd Pixel_6_API_34
# Start headless (for CI)
emulator -avd Pixel_6_API_34 -no-window -no-audio
# Install APK
adb install app.apk
# Uninstall app
adb uninstall com.example.myappReal Device Testing
For real devices, enable Developer Options and USB Debugging:
- Go to Settings → About Phone
- Tap "Build Number" 7 times
- Go to Settings → Developer Options
- Enable "USB Debugging"
- Connect device and accept debugging prompt
const capabilities = {
platformName: 'Android',
'appium:automationName': 'UiAutomator2',
'appium:deviceName': 'Android Device',
'appium:udid': 'your-device-serial', // From `adb devices`
'appium:app': '/path/to/app.apk',
};Comparison:
| Aspect | Emulator | Real Device |
|---|---|---|
| Setup | Easy | Requires device |
| Speed | Good with HAXM/Hypervisor | Native performance |
| Accuracy | May miss device-specific bugs | True behavior |
| CI/CD | Ideal | Requires device farm |
| Parallel | Multiple instances | One test per device |
| Hardware | Simulated | Real sensors |
Use emulators for rapid development and CI runs, but always validate critical flows on real devices before release.
Android Capabilities Configuration
Essential Capabilities
const capabilities = {
// Platform
platformName: 'Android',
'appium:automationName': 'UiAutomator2',
'appium:deviceName': 'Pixel_6_API_34',
// App
'appium:app': '/path/to/app.apk',
// OR for installed apps:
'appium:appPackage': 'com.example.myapp',
'appium:appActivity': '.MainActivity',
// Session behavior
'appium:noReset': false,
'appium:fullReset': false,
};Advanced Capabilities
const capabilities = {
// ... essential capabilities ...
// Timeouts
'appium:newCommandTimeout': 300,
'appium:adbExecTimeout': 60000,
'appium:androidInstallTimeout': 90000,
// App handling
'appium:autoGrantPermissions': true,
'appium:allowTestPackages': true,
'appium:enforceAppInstall': true,
// Chrome/WebView
'appium:chromeOptions': {
args: ['--disable-popup-blocking']
},
'appium:chromedriverExecutable': '/path/to/chromedriver',
// Device settings
'appium:disableWindowAnimation': true,
'appium:skipUnlock': true,
// Network
'appium:ignoreUnimportantViews': true,
};Getting App Package and Activity
If you don't know your app's package and activity:
# With app open on device
adb shell dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'
# Or install the APK and check
aapt dump badging app.apk | grep -E 'package:|launchable-activity:'Android Locator Strategies
Resource ID (Recommended)
The most reliable Android locator:
// Full resource ID
const element = await driver.$('id=com.example.myapp:id/login_button');
// Short form (package prefix optional)
const element = await driver.$('id=login_button');Accessibility ID
Cross-platform compatible:
// contentDescription attribute in Android
const element = await driver.$('~login_button');UiSelector
Android's native powerful selector:
// By text
await driver.$('android=new UiSelector().text("Login")');
// By partial text
await driver.$('android=new UiSelector().textContains("Log")');
// By description
await driver.$('android=new UiSelector().description("Submit button")');
// By class and text
await driver.$('android=new UiSelector().className("android.widget.Button").text("OK")');
// By resource ID
await driver.$('android=new UiSelector().resourceId("com.example:id/submit")');
// Scrolling to find element
await driver.$('android=new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text("Target"))');
// Child element
await driver.$('android=new UiSelector().className("android.widget.ListView").childSelector(new UiSelector().text("Item 1"))');UiSelector Cheat Sheet
| Method | Description |
|---|---|
text("exact") | Match exact text |
textContains("partial") | Match partial text |
textStartsWith("start") | Match text prefix |
description("desc") | Match content description |
resourceId("id") | Match resource ID |
className("class") | Match class name |
index(n) | Match by index |
instance(n) | Nth instance |
enabled(true) | Enabled state |
clickable(true) | Clickable state |
scrollable(true) | Scrollable elements |
XPath
Works but slower than native selectors:
await driver.$('//android.widget.Button[@text="Login"]');
await driver.$('//android.widget.EditText[@resource-id="username"]');⚠️
Prefer resource ID or UiSelector over XPath for Android tests. XPath performance is significantly worse, especially on complex layouts.
Android-Specific Actions
Touch Gestures
// Single tap
await element.click();
// Long press
await driver.touchAction([
{ action: 'longPress', element },
{ action: 'release' }
]);
// Swipe/Scroll
await driver.touchAction([
{ action: 'press', x: 500, y: 1500 },
{ action: 'wait', ms: 1000 },
{ action: 'moveTo', x: 500, y: 500 },
{ action: 'release' }
]);
// Double tap
await driver.touchAction([
{ action: 'tap', element },
{ action: 'tap', element }
]);
// Drag and drop
await driver.touchAction([
{ action: 'longPress', element: sourceElement },
{ action: 'moveTo', element: targetElement },
{ action: 'release' }
]);Scroll Operations
// Scroll using UiScrollable (recommended)
await driver.$('android=new UiScrollable(new UiSelector().scrollable(true)).scrollToEnd(5)');
await driver.$('android=new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text("Target"))');
// Scroll using mobile command
await driver.execute('mobile: scroll', {
direction: 'down',
strategy: 'accessibility id',
selector: 'target_element'
});
// Scroll specific container
const list = await driver.$('id=list_view');
await driver.execute('mobile: swipeGesture', {
elementId: list.elementId,
direction: 'down',
percent: 0.75
});Key Events
// Press hardware keys
await driver.pressKeyCode(4); // Back
await driver.pressKeyCode(3); // Home
await driver.pressKeyCode(187); // Recent apps
await driver.pressKeyCode(82); // Menu
await driver.pressKeyCode(24); // Volume Up
await driver.pressKeyCode(25); // Volume Down
// Or use named keys
await driver.executeScript('mobile: pressKey', [{ keycode: 4 }]);Text Input
// Set value (replaces content)
await element.setValue('new text');
// Add value (appends)
await element.addValue(' appended');
// Clear
await element.clearValue();
// Hide keyboard
await driver.hideKeyboard();
// Check keyboard visibility
const isShown = await driver.isKeyboardShown();Handling Android System Features
Permission Handling
// Auto-grant permissions (in capabilities)
'appium:autoGrantPermissions': true
// Or handle manually
async function handlePermission() {
try {
const allowButton = await driver.$('id=com.android.permissioncontroller:id/permission_allow_button');
if (await allowButton.isDisplayed()) {
await allowButton.click();
}
} catch (e) {
// No permission dialog
}
}Toast Messages
// Capture toast (requires UiAutomator2)
const toast = await driver.$('android=new UiSelector().className("android.widget.Toast")');
const toastText = await toast.getText();
// Or use getPageSource and parse
const source = await driver.getPageSource();
// Parse for toast contentNotifications
// Open notification shade
await driver.openNotifications();
// Interact with notification
const notification = await driver.$('android=new UiSelector().text("Notification title")');
await notification.click();
// Close notification shade
await driver.pressKeyCode(4); // Back keyApp State Management
// Background app
await driver.background(5); // 5 seconds
// Terminate app
await driver.terminateApp('com.example.myapp');
// Launch app
await driver.activateApp('com.example.myapp');
// Check if app is running
const state = await driver.queryAppState('com.example.myapp');
// Returns: 0=not installed, 1=not running, 2=background, 3=suspended, 4=foreground
// Reset app data
await driver.reset();
// Clear app data via ADB
await driver.execute('mobile: shell', {
command: 'pm clear com.example.myapp'
});ADB Commands in Tests
Execute ADB commands directly in tests:
// Execute shell command
const result = await driver.execute('mobile: shell', {
command: 'pm list packages'
});
// Get device info
const deviceInfo = await driver.execute('mobile: shell', {
command: 'getprop ro.product.model'
});
// Take screenshot via ADB
await driver.execute('mobile: shell', {
command: 'screencap -p /sdcard/screen.png'
});
// Push file to device
await driver.pushFile('/sdcard/test.txt', Buffer.from('content').toString('base64'));
// Pull file from device
const fileContent = await driver.pullFile('/sdcard/test.txt');
// Get battery info
const battery = await driver.execute('mobile: shell', {
command: 'dumpsys battery'
});Debugging Android Tests
Logcat
View Android logs during test:
# In terminal during test
adb logcat
# Filter by tag
adb logcat -s "MyApp"
# Filter by priority
adb logcat *:E # Errors onlyIn tests:
// Get logcat output
const logs = await driver.execute('mobile: getDeviceLogEntries');
// Or via ADB command
const logs = await driver.execute('mobile: shell', {
command: 'logcat -d -v brief'
});Screenshots
// Take screenshot
await driver.saveScreenshot('./screenshot.png');
// Screenshot as base64
const screenshot = await driver.takeScreenshot();Screen Recording
// Start recording
await driver.startRecordingScreen({
timeLimit: 180, // Max 3 minutes
videoSize: '720x1280',
bitRate: 4000000
});
// Stop and save
const video = await driver.stopRecordingScreen();
const fs = require('fs');
fs.writeFileSync('recording.mp4', Buffer.from(video, 'base64'));Page Source
// Get UI hierarchy XML
const source = await driver.getPageSource();
console.log(source);
// Save to file
fs.writeFileSync('ui_dump.xml', source);Layout Inspector
Use Android Studio's Layout Inspector for visual debugging:
- Open Android Studio
- View → Tool Windows → Layout Inspector
- Select your app process
- Explore the view hierarchy visually
Performance Optimization
Speed Up Test Startup
const capabilities = {
// Skip uninstalling/reinstalling
'appium:noReset': true,
// Skip unlocking device
'appium:skipUnlock': true,
// Disable animations
'appium:disableWindowAnimation': true,
// Skip signature verification
'appium:enforceAppInstall': false,
// Reduce server startup time
'appium:skipServerInstallation': true,
};Faster Element Location
// Bad: XPath
await driver.$('//android.widget.Button[@text="Login"]');
// Good: Resource ID
await driver.$('id=login_button');
// Good: UiSelector
await driver.$('android=new UiSelector().resourceId("login_button")');Disable Animation Globally
# Via ADB (persistent until reboot)
adb shell settings put global window_animation_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global animator_duration_scale 0Parallel Execution
Run multiple emulators for parallel tests:
# Start multiple emulators
emulator -avd Pixel_6_API_34 -port 5554 &
emulator -avd Pixel_6_API_34 -port 5556 &
# Each shows as separate device
adb devices
# emulator-5554 device
# emulator-5556 device// In test config
const capabilities = [
{ 'appium:udid': 'emulator-5554', /* ... */ },
{ 'appium:udid': 'emulator-5556', /* ... */ },
];Android's diverse ecosystem means testing on multiple API levels and device configurations matters. By mastering UiAutomator2's capabilities, leveraging ADB for debugging, and following optimization practices, you can build comprehensive Android test suites that catch real-world issues before your users do.
Quiz on Android Testing with Appium
Your Score: 0/10
Question: Which driver does Appium use for Android automation?
Continue Reading
Frequently Asked Questions (FAQs) / People Also Ask (PAA)
Can I run Android tests on Windows, macOS, and Linux?
How do I find the package name and activity name for my app?
Why are my Android tests running slowly?
How do I test on multiple Android versions?
How do I handle Android permission dialogs?
Can I test apps that aren't on the Play Store?
How do I capture toast messages in tests?
What's the difference between UiAutomator2 and Espresso drivers?