agentleFS
MathiasPaulenkoMathiasPaulenko/ai-toolkit/skills/appium-mobile/SKILL.md

appium-mobile

Skill2 starsChanged 3 months agoMITView on GitHub

Mobile automation with Appium: iOS and Android, gestures, device farms (BrowserStack, Sauce Labs), parallel execution, and CI/CD.

Files
SKILL.md

Appium Mobile

Mobile test automation with Appium for iOS and Android.

1. Architecture

Appium Test Suite
├── tests/
│   ├── conftest.py         # Driver fixtures
│   ├── test_login.py
│   ├── test_checkout.py
│   └── pages/
│       ├── base_page.py
│       ├── login_page.py
│       └── home_page.py
├── capabilities/
│   ├── android.json        # Device configs
│   └── ios.json
└── pytest.ini

2. Driver Setup

# conftest.py
import pytest
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.options.ios import XCUITestOptions

@pytest.fixture
def android_driver():
    options = UiAutomator2Options()
    options.platform_name = "Android"
    options.device_name = "Pixel_7_API_34"
    options.app = "./app/build/app-debug.apk"
    options.automation_name = "UiAutomator2"
    options.no_reset = False

    driver = webdriver.Remote("http://localhost:4723", options=options)
    yield driver
    driver.quit()

@pytest.fixture
def ios_driver():
    options = XCUITestOptions()
    options.platform_name = "iOS"
    options.device_name = "iPhone 15"
    options.platform_version = "17.0"
    options.app = "./app/build/ios-simulator.app"
    options.automation_name = "XCUITest"

    driver = webdriver.Remote("http://localhost:4723", options=options)
    yield driver
    driver.quit()

3. Page Object Model

# pages/login_page.py
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class LoginPage:
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    def find_username(self):
        return self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "username")

    def find_password(self):
        return self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "password")

    def find_login_button(self):
        return self.driver.find_element(AppiumBy.ID, "com.example.app:id/login")

    def login(self, user, password):
        self.find_username().send_keys(user)
        self.find_password().send_keys(password)
        self.find_login_button().click()

    def is_logged_in(self):
        return self.wait.until(
            EC.presence_of_element_located(
                (AppiumBy.ACCESSIBILITY_ID, "welcome")
            )
        )

4. Locators

StrategyAndroidiOSBest Practice
ACCESSIBILITY_IDcontent-descaccessibilityLabelPreferred — stable
IDresource-idname (limited)Good for unique IDs
XPATHAny XML pathAny XML pathSlow; use sparingly
CLASS_NAMEandroid.widget.ButtonXCUIElementTypeButtonFor type filtering
IOS_PREDICATEN/Atype == 'XCUIElementTypeButton'iOS-specific, fast
ANDROID_UIAUTOMATORnew UiSelector().text("OK")N/AAndroid-specific

5. Gestures & Interactions

from appium.webdriver.common.touch_action import TouchAction
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput

# Tap
TouchAction(driver).tap(x=100, y=200).perform()

# Swipe / Scroll
def swipe(driver, start, end):
    action = TouchAction(driver)
    action.press(x=start[0], y=start[1])
    action.wait(ms=500)
    action.move_to(x=end[0], y=end[1])
    action.release().perform()

# W3C Actions (modern)
actions = ActionBuilder(driver)
pointer = PointerInput("touch", "finger")
actions.pointer_action.move_to_location(100, 500)
actions.pointer_action.pointer_down()
actions.pointer_action.move_to_location(100, 200)
actions.pointer_action.pointer_up()
actions.perform()

# Wait for element
element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located(
        (AppiumBy.ACCESSIBILITY_ID, "loading-complete")
    )
)

6. Device Farms

BrowserStack

options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "Samsung Galaxy S23"
options.app = "bs://<app-id>"

# bs-local for local testing
from browserstack.local import Local
local = Local()
local.start({"key": BROWSERSTACK_KEY})

driver = webdriver.Remote(
    f"https://{USER}:{KEY}@hub-cloud.browserstack.com/wd/hub",
    options=options
)

Sauce Labs

# Replace with your Sauce Labs credentials
# Endpoint format: ondemand.saucelabs.com/wd/hub
driver = webdriver.Remote(
    "https://ondemand.example.com/wd/hub",
    options=options
)

7. Parallel Execution

# pytest-xdist with device-specific ports
pytest -n 2 --dist loadgroup \
  --capabilities '{"udid": "emulator-5554"}' \
  --capabilities '{"udid": "emulator-5556"}'

8. CI/CD

# .github/workflows/mobile-tests.yml
name: Mobile Tests
on: [push]
jobs:
  android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: 34
          script: |
            adb install app/build/app-debug.apk
            pytest tests/ --driver=android

9. Anti-Patterns

Anti-PatternFix
XPath without accessibility IDsAdd content-desc / accessibilityLabel
Hardcoded waitsUse WebDriverWait with expected conditions
Testing on emulators onlyRun on real devices via cloud farm
No app state cleanupUninstall or clear data between tests
Ignoring rotationTest landscape + portrait
No offline scenariosToggle airplane mode / network throttling

10. Related Resources

  • Skills: android-native, playwright-e2e
  • Prompts: generate-mobile-test-strategy

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.