CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/appium-testing

Wires Appium for cross-platform mobile UI automation - uses the WebDriver protocol, picks a driver per platform (XCUITest for iOS, UiAutomator2 / Espresso for Android, Mac2 for macOS, Windows for desktop), authors tests in JS / Python / Java / Ruby / .NET, configures `desiredCapabilities`, runs against simulators / emulators / device farms. Use when a single test suite must cover both iOS and Android, or when the team's stack is multi-platform (iOS + Android + Mac + Windows).

80

Quality

100%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files
name:
appium-testing
description:
Wires Appium for cross-platform mobile UI automation - uses the WebDriver protocol, picks a driver per platform (XCUITest for iOS, UiAutomator2 / Espresso for Android, Mac2 for macOS, Windows for desktop), authors tests in JS / Python / Java / Ruby / .NET, configures `desiredCapabilities`, runs against simulators / emulators / device farms. Use when a single test suite must cover both iOS and Android, or when the team's stack is multi-platform (iOS + Android + Mac + Windows).

appium-testing

The driver model is the load-bearing concept: Appium itself is the WebDriver protocol; per-platform automation is a driver (XCUITest driver talks to iOS, UiAutomator2 / Espresso to Android, Mac2 to macOS, Windows to Windows desktop). Client libraries exist for JavaScript, Python, Java, Ruby, and .NET (per appium-docs).

When to use

  • A team needs one test suite to cover iOS + Android.
  • The product is multi-platform (mobile + desktop + TV) and one framework should drive all of them.
  • Existing Selenium expertise transfers (same WebDriver mental model).
  • Device-farm integration matters (BrowserStack / Sauce Labs / AWS Device Farm all support Appium natively).

If the app is iOS-only, xcuitest-suite is lighter (no external server). For React Native specifically, detox-testing is faster.

Step 1 - Install Appium server + driver

npm install -g appium

# Install per-platform drivers:
appium driver install xcuitest         # iOS
appium driver install uiautomator2     # Android
appium driver install espresso         # Android (alternative; gray-box)
appium driver install mac2             # macOS desktop
appium driver install windows          # Windows desktop

# Verify:
appium driver list

Step 2 - Start the server

appium server -p 4723

The default port is 4723; client tests connect to http://localhost:4723 (no /wd/hub suffix in Appium 2.x).

Step 3 - Author a test (JavaScript example)

import { remote } from 'webdriverio';

const capabilities = {
  platformName: 'Android',
  'appium:automationName': 'UiAutomator2',
  'appium:deviceName': 'Android Emulator',
  'appium:app': '/path/to/app.apk',
};

describe('Cart flow', () => {
  let driver;

  before(async () => {
    driver = await remote({
      hostname: 'localhost',
      port: 4723,
      capabilities,
    });
  });

  after(async () => {
    await driver.deleteSession();
  });

  it('adds an item to cart', async () => {
    const addButton = await driver.$('id=add_to_cart_button');
    await addButton.click();

    const cartCount = await driver.$('id=cart_count');
    const text = await cartCount.getText();
    expect(text).toBe('1');
  });
});

The automationName capability picks the driver: UiAutomator2 (Android), XCUITest (iOS), Espresso (Android gray-box), Mac2, Windows.

Step 4 - Capabilities catalog

CapabilityUse
platformNameiOS / Android / Mac / Windows
appium:automationNameDriver name (XCUITest / UiAutomator2 / Espresso etc.)
appium:deviceNameLogical device name (Simulator / Emulator / specific)
appium:platformVersionOS version (e.g. 14.0)
appium:appPath to .apk / .ipa
appium:bundleId / appium:appPackagePre-installed app
appium:noResetDon't reinstall app between sessions
appium:newCommandTimeoutServer idle timeout (default 60s)

For device-farm runs, capabilities also include the farm's authentication tokens / device-pool selection.

Step 5 - Selector strategies (cross-platform)

// By accessibility ID - the most cross-platform
await driver.$('~login-button').click();   // ~ prefix in WebdriverIO

// By id - works for both platforms with platform-prefixed IDs
await driver.$('id=add_to_cart_button');     // Android
await driver.$('id=add-to-cart-button');     // iOS uses accessibility ID

// XPath - works everywhere; brittle (selector-quality anti-pattern)
await driver.$('//XCUIElementTypeButton[@name="Submit"]');

// Image-based locator (Appium-specific) - fallback when no IDs
await driver.findElementByImage('./assets/login-button.png');

The cross-platform strategy: production code sets the same accessibility ID on both iOS (accessibilityIdentifier) and Android (contentDescription or resource-id). Tests use one selector for both.

Step 6 - Run

# Local with one platform
WDIO_OS=android wdio run wdio.conf.js

# Multi-platform: WebdriverIO multi-capability config
# wdio.conf.js
exports.config = {
    capabilities: [
        { platformName: 'iOS', 'appium:platformVersion': '17.4', ...iosCaps },
        { platformName: 'Android', 'appium:platformVersion': '14', ...androidCaps },
    ],
    runner: 'local',
    services: ['appium'],
};

The services: ['appium'] line auto-starts the Appium server inside the test runner - no separate server step.

Step 7 - CI integration

# Multi-platform matrix
jobs:
  ui-tests:
    strategy:
      matrix:
        platform: [iOS, Android]
    runs-on: ${{ matrix.platform == 'iOS' && 'macos-15' || 'ubuntu-latest' }}
    steps:
      - uses: actions/checkout@v5

      - if: matrix.platform == 'Android'
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: 34
          script: WDIO_OS=android npx wdio run wdio.conf.js

      - if: matrix.platform == 'iOS'
        run: |
          xcrun simctl boot 'iPhone 15'
          WDIO_OS=ios npx wdio run wdio.conf.js

Anti-patterns

Anti-patternWhy it failsFix
Per-platform separate test codeDefeats Appium's reuse value.One test, multi-capability config (Step 6).
XPath selectors as defaultSlowest selector strategy; brittle to UI tree changes.Accessibility IDs first (Step 5).
Hard-coded device name ('iPhone 15')Test fails when sim doesn't exist.Use genericDevice: true or generated capabilities.
Ignoring noReset between testsApp state leaks; flaky.noReset: false (default) reinstalls per session.
One mega-test using before for the whole suiteSuite-long flake; one tiny issue restarts everything.Per-test beforeEach reset; small focused tests.
Mixing Appium 1.x and 2.x docsServer URL / capability format differ; failures cryptic.Pin Appium 2.x; use Appium 2.x docs only.

Limitations

  • External server. Appium's HTTP-protocol design adds latency vs in-process frameworks (Espresso, XCUITest) - typical 100-300ms per command.
  • Per-driver feature gaps. Some features are XCUITest-driver only or UiAutomator2-driver only; cross-platform tests must avoid them.
  • Device-farm cost. Real devices on cloud farms charge per-minute; matrix runs add up.
  • Setup complexity. Server + drivers + Android SDK + Xcode command-line tools = many dependencies. Containerize where possible.

References

  • app - Appium overview: WebDriver protocol, driver architecture, supported platforms (iOS, Android, Tizen, browser, desktop, TV), client libraries (JS / Python / Java / Ruby / .NET).
  • xcuitest-suite, espresso-suite - per-platform native alternatives.
  • detox-testing, maestro-flows - cross-platform alternatives with different trade-offs.
  • mobile-device-matrix-toolkit - orchestrates Appium-driven matrix runs across simulators / emulators / farms.
Workspace
testland
Visibility
Public
Created
Last updated
Publish Source
GitHub
Badge
testland/appium-testing badge