Appium Testing with DeviceLab
Run Appium tests on remote Android and iOS devices with DeviceLab. This guide covers WebDriver setup, capability configuration, and CI/CD integration. DeviceLab handles app installation and device allocation automatically - your existing tests work with minimal changes.
Overview
DeviceLab lets you run Appium tests on remote devices. The app file is transferred securely to the device node and automatically configured - you don’t need to handle app installation in your test code.
Key points:
- Pass your app to the test node via
--appflag - DeviceLab handles app installation automatically
- Your tests connect to
http://localhost:4723/wd/hub- no app path needed in test code
How It Works
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Your Machine │ │ DeviceLab │ │ Device Node │
│ │ │ Server │ │ (Remote) │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ 1. Run testNode │────▶│ 2. Routes to │────▶│ 3. Receives APK │
│ with --app │ │ device node │ │ & starts │
│ │ │ │ │ Appium │
│ │ │ │ │ │
│ 4. Run tests │◀────│◀────────────────│◀────│ 5. Appium ready │
│ (localhost) │ │ (tunneled) │ │ on device │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Quick Start
Prerequisites
- DeviceLab account with install key
- Device node running with connected Android device
- Java 8+ and Maven (for Java tests) OR Python 3+ (for Python tests)
- Appium client library in your project
Step 1: Start Appium Server via DeviceLab
curl -fsSL https://app.devicelab.dev/test-node/YOUR_ORG_KEY | sh -s -- \
--framework appium \
--app ./path/to/your/app.apk
Wait for: ✅ Appium server ready on http://localhost:4723
Step 2: Run Your Tests
In a separate terminal:
# Java/Maven
mvn clean test
# Python
pytest
Writing Your Tests
Important: DO NOT Set App Capability
DeviceLab automatically handles the app. Your test code should NOT include:
// ❌ WRONG - Don't do this
caps.setCapability("app", "/path/to/app.apk");
caps.setCapability("appium:app", "/path/to/app.apk");
Java Example (Correct)
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;
public class BaseTest {
protected AndroidDriver driver;
public void setUp() throws Exception {
DesiredCapabilities caps = new DesiredCapabilities();
// ✅ Set these capabilities
caps.setCapability("platformName", "Android");
caps.setCapability("automationName", "UiAutomator2");
// ❌ DO NOT set "app" - DeviceLab handles it automatically
// caps.setCapability("app", "..."); // REMOVE THIS
// Optional: Set app package/activity if needed
caps.setCapability("appPackage", "com.example.myapp");
caps.setCapability("appActivity", "com.example.myapp.MainActivity");
// Connect to DeviceLab's local Appium server
driver = new AndroidDriver(new URL("http://localhost:4723/wd/hub"), caps);
}
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
Python Example (Correct)
from appium import webdriver
from appium.options.android import UiAutomator2Options
class TestBase:
def setup_method(self):
options = UiAutomator2Options()
# ✅ Set these capabilities
options.platform_name = "Android"
# ❌ DO NOT set "app" - DeviceLab handles it automatically
# options.app = "/path/to/app.apk" # REMOVE THIS
# Optional: Set app package/activity if needed
options.app_package = "com.example.myapp"
options.app_activity = "com.example.myapp.MainActivity"
# Connect to DeviceLab's local Appium server
self.driver = webdriver.Remote(
"http://localhost:4723/wd/hub",
options=options
)
def teardown_method(self):
if self.driver:
self.driver.quit()
Capabilities Reference
Required Capabilities
| Capability | Value | Description |
|---|---|---|
platformName |
Android |
Target platform |
automationName |
UiAutomator2 |
Automation engine |
Optional Capabilities
| Capability | Example | Description |
|---|---|---|
appPackage |
com.example.app |
App package name |
appActivity |
com.example.MainActivity |
Launch activity |
noReset |
true |
Don’t reset app state |
fullReset |
false |
Don’t uninstall app |
newCommandTimeout |
300 |
Session timeout (seconds) |
Capabilities Set by DeviceLab (DO NOT Override)
These are automatically configured by DeviceLab:
| Capability | Description |
|---|---|
appium:app |
Path to APK on device node |
appium:udid |
Device serial number |
appium:systemPort |
UiAutomator2 port (Android) |
appium:wdaLocalPort |
WebDriverAgent port (iOS) |
appium:mjpegServerPort |
Screenshot streaming port |
Command Line Options
curl -fsSL https://app.devicelab.dev/test-node/YOUR_ORG_KEY | sh -s -- \
--framework appium \
--app ./MyApp.apk \
[OPTIONS]
| Option | Description | Default |
|---|---|---|
--framework appium |
Use Appium framework | Required |
--app <path> |
Path to APK/IPA file | Required |
--platform android |
Target platform | android |
--device-names <name> |
Specific device name | Any available |
--device-count <n> |
Number of devices | 1 |
Sample Project Structure
my-appium-tests/
├── src/
│ └── test/
│ └── java/
│ ├── BaseTest.java # Driver setup (NO app capability)
│ └── MyAppTest.java # Your test cases
├── pom.xml # Maven dependencies
└── MyApp.apk # Your Android app
pom.xml Dependencies
<dependencies>
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>8.6.0</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.15.0</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.8.0</version>
</dependency>
</dependencies>
Troubleshooting
“App not found” Error
Cause: You set app capability in your test code, overriding DeviceLab’s configuration.
Fix: Remove app capability from your test code.
“Could not start session” Error
Cause: Appium server not ready or wrong URL.
Fix:
- Wait for
✅ Appium server readymessage - Use
http://localhost:4723/wd/hubas server URL
“No device available” Error
Cause: No device node running or all devices busy.
Fix:
- Start a device node:
curl -fsSL https://app.devicelab.dev/device-node/YOUR_ORG_KEY | sh - Check dashboard for available devices
Connection Timeout
Cause: Network issues or server not responding.
Fix:
- Check if test node is still running
- Check firewall settings
CI/CD Integration
Use the DeviceLab Appium Action for GitHub Actions.
Basic Example
name: Mobile Testing
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Your build steps here...
- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: app-release
path: path/to/your/app.apk
test:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Start DeviceLab Appium
uses: izinga/devicelab_appium@v1
with:
devicelab-url: 'https://app.devicelab.dev/test-node/YOUR_ORG_KEY'
apk-artifact-name: 'app-release'
- name: Run Appium Tests
run: |
# Your tests connect to http://localhost:4723/wd/hub
pytest tests/
Advanced Example
- name: Start DeviceLab Appium
id: testnode
uses: izinga/devicelab_appium@v1
with:
devicelab-url: 'https://app.devicelab.dev/test-node/YOUR_ORG_KEY'
apk-artifact-name: 'my-app-build'
apk-path: 'app-debug.apk'
test-node-port: '4723'
wait-timeout: '600'
- name: Run Tests
run: |
echo "Test node running at: ${{ steps.testnode.outputs.test-node-url }}"
pytest tests/
- name: Cleanup
if: always()
run: |
docker stop ${{ steps.testnode.outputs.container-name }} || true
Action Inputs
| Input | Required | Default | Description |
|---|---|---|---|
devicelab-url |
Yes | - | DeviceLab test node URL |
apk-artifact-name |
Yes | - | Name of artifact containing APK |
apk-path |
No | app-release.apk |
Path to APK within artifact |
test-node-port |
No | 4723 |
Port for test node server |
wait-timeout |
No | 300 |
Timeout in seconds |
Action Outputs
| Output | Description |
|---|---|
test-node-url |
URL of the running test node |
container-name |
Docker container name |
Requirements
- Linux runner (Ubuntu recommended)
- Docker available in the runner
- APK artifact from a previous job
Best Practices
- Don’t set app path - Let DeviceLab handle it via
--appflag - Use localhost:4723/wd/hub - DeviceLab tunnels the connection
- Set reasonable timeouts -
newCommandTimeout: 300recommended - Use appPackage/appActivity - For faster app launch
- Handle driver cleanup - Always call
driver.quit()in teardown
Next Steps
- Running Tests Guide
- Test Node CLI Reference
- Maestro Testing Guide - Alternative UI testing framework