AppiumOptions and webdriver.Remote in Python

6 min read

Appium Python Client 3.x introduced typed options classes — UiAutomator2Options for Android and XCUITestOptions for iOS. They replace the old dictionary-based capability approach with Python dataclasses that have IDE autocompletion and snake_case property names.

UiAutomator2Options

from appium.options import UiAutomator2Options
 
options = UiAutomator2Options()
 
# Required
options.device_name = "emulator-5554"
options.platform_version = "13"
options.app = "/absolute/path/to/app-debug.apk"
 
# Useful defaults
options.auto_grant_permissions = True
options.no_reset = True
options.new_command_timeout = 60
 
# App-specific (skip options.app if these are set)
options.app_package = "com.example.myapp"
options.app_activity = ".MainActivity"

Properties are snake_case in Python — auto_grant_permissions, not autoGrantPermissions. The client translates them to the correct W3C capability names before sending to the server.

XCUITestOptions

from appium.options import XCUITestOptions
 
options = XCUITestOptions()
 
options.device_name = "iPhone 15"
options.platform_version = "17.0"
options.app = "/path/to/MyApp.app"  # simulator build (.app)
options.no_reset = True
options.wda_startup_retries = 3
options.simulator_startup_timeout = 120_000  # milliseconds

For real devices:

options.udid = "00008030-001A234567890012"
options.xcode_org_id = "TEAM_ID_HERE"
options.xcode_signing_id = "Apple Development"

Capability dictionary — the old way

For context, this is the pre-3.x approach:

# Old — avoid in new code
desired_caps = {
    "platformName": "Android",
    "appium:deviceName": "emulator-5554",
    "appium:automationName": "UiAutomator2",
    "appium:app": "/path/to/app.apk",
}
driver = webdriver.Remote("http://127.0.0.1:4723", desired_capabilities=desired_caps)

The typed options classes are preferred because typos in capability names are caught at IDE time, not at session creation time.

Passing options to the driver

from appium import webdriver
 
driver = webdriver.Remote(
    command_executor="http://127.0.0.1:4723",
    options=options
)

The command_executor is the Appium server URL. With appium --base-path /, this is http://127.0.0.1:4723 (no /wd/hub suffix).

Loading options from a JSON config

For teams that change device configurations frequently, read options from a JSON file:

{
  "android": {
    "device_name": "emulator-5554",
    "platform_version": "13",
    "app": "apps/app-debug.apk",
    "auto_grant_permissions": true,
    "no_reset": true
  },
  "ios": {
    "device_name": "iPhone 15",
    "platform_version": "17.0",
    "app": "apps/MyApp.app",
    "no_reset": true
  }
}
import json
from appium.options import UiAutomator2Options, XCUITestOptions
 
def load_options(platform: str):
    with open("config/devices.json") as f:
        config = json.load(f)
    
    data = config[platform.lower()]
    
    if platform.lower() == "android":
        options = UiAutomator2Options()
        for key, value in data.items():
            setattr(options, key, value)
        return options
    else:
        options = XCUITestOptions()
        for key, value in data.items():
            setattr(options, key, value)
        return options

App reset strategies

PropertyEffect
no_reset=TrueSkip uninstall/reinstall — fastest
no_reset=False (default)Reinstall the app each session
full_reset=TrueUninstall + wipe app data

For most tests, no_reset=True combined with activate_app() / terminate_app() in test setup gives fast session creation while still starting the app fresh.

BrowserStack capabilities

When running on BrowserStack, add the bstack:options capability:

from appium.options import UiAutomator2Options
 
options = UiAutomator2Options()
options.device_name = "Samsung Galaxy S23"
options.platform_version = "13.0"
options.app = "bs://your-app-hash"
 
options.load_capabilities({
    "bstack:options": {
        "projectName": "Mobile Suite",
        "buildName": "CI Build",
        "networkLogs": True,
        "deviceLogs": True,
    }
})

load_capabilities() lets you set arbitrary capability keys that don't have typed properties.

// tip to track lessons you complete and pick up where you left off across devices.