Skip to main content

Reusable Appium 2.x + pytest mobile test framework

Project description

appium-pytest-kit

appium-pytest-kit is a reusable Appium 2.x + pytest framework library for Python 3.11+.

  • pip install appium-pytest-kit (or install from GitHub — see below)
  • appium-pytest-kit-init to bootstrap configuration, or --framework to scaffold a full project
  • Write tests immediately with built-in fixtures and zero boilerplate

Full documentation: DOCUMENTATION.md


Installation

From PyPI (once published)

pip install appium-pytest-kit

From GitHub

# latest main branch
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git

# specific branch
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git@main

# specific tag
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git@v0.1.0

Local clone (editable, for development)

git clone https://github.com/gianlucasoare/appium-pytest-kit.git
cd appium-pytest-kit
pip install -e ".[dev]"

Quickstart

python -m venv .venv
source .venv/bin/activate
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git
appium-pytest-kit-init          # creates .env with starter config
# or scaffold a full project:
appium-pytest-kit-init --framework --root my-project
pytest -q

Edit .env with your device and app details, then write tests.


Step-by-step: test a real app in 5 minutes

This example tests the Android Calculator on an emulator. See DOCUMENTATION.md for the full iOS walkthrough and all options.

1. Start Appium and an emulator

appium &
emulator -avd Pixel_7_API_33 &
adb devices    # confirm emulator-5554 is listed

2. Configure .env

APP_PLATFORM=android
APP_APPIUM_URL=http://127.0.0.1:4723
APP_DEVICE_NAME=emulator-5554
APP_PLATFORM_VERSION=13
APP_APP_PACKAGE=com.google.android.calculator
APP_APP_ACTIVITY=com.android.calculator2.Calculator
APP_NO_RESET=true

3. Write a test

# tests/test_calculator.py
import pytest
from appium.webdriver.common.appiumby import AppiumBy

BTN_2      = (AppiumBy.ACCESSIBILITY_ID, "2")
BTN_PLUS   = (AppiumBy.ACCESSIBILITY_ID, "plus")
BTN_3      = (AppiumBy.ACCESSIBILITY_ID, "3")
BTN_EQUALS = (AppiumBy.ACCESSIBILITY_ID, "equals")
RESULT     = (AppiumBy.RESOURCE_ID, "com.google.android.calculator:id/result_final")


@pytest.mark.integration
def test_addition(actions):
    actions.tap(BTN_2)
    actions.tap(BTN_PLUS)
    actions.tap(BTN_3)
    actions.tap(BTN_EQUALS)
    assert actions.text(RESULT) == "5"

4. Run it

pytest -m integration -v

Built-in fixtures

Fixture Scope Description
settings session Resolved AppiumPytestKitSettings — access any config field
appium_server session Server URL and whether it is framework-managed
driver function Live appium.webdriver.Remote, quit automatically after each test
waiter function Explicit waits with WaitTimeoutError on timeout
actions function High-level UI helpers: tap, type_text, text, exists, swipe, and more

Session modes

Control driver lifecycle per test or across the whole session:

APP_SESSION_MODE=clean          # default: fresh driver per test
APP_SESSION_MODE=clean-session  # shared driver, app reset between tests
APP_SESSION_MODE=debug          # shared driver, no reset (fast local debugging)

Device resolution (3-tier priority)

  1. ExplicitAPP_DEVICE_NAME / APP_UDID set in .env or CLI
  2. ProfileAPP_DEVICE_PROFILE=pixel7 resolved from data/devices.yaml
  3. Auto-detectadb devices (Android) or xcrun simctl / xctrace (iOS)
# Use a named profile from data/devices.yaml
pytest --app-device-profile pixel7

# Auto-detect (no device settings needed)
pytest

Failure diagnostics

On test failure the framework automatically captures:

  • Screenshotartifacts/screenshots/<test_id>.png
  • Page sourceartifacts/pagesource/<test_id>.xml
  • Video (if policy allows) → artifacts/videos/<test_id>.mp4
APP_VIDEO_POLICY=never    # default
APP_VIDEO_POLICY=failed   # record and save only on failure
APP_VIDEO_POLICY=always   # record every test
APP_ARTIFACTS_DIR=artifacts

Allure attachments are added automatically when allure-pytest is installed.


Configuration

Settings are loaded from .env → environment variables → CLI flags (highest wins).

pytest --app-platform ios
pytest --app-device-name "Pixel 7" --app-platform-version 13
pytest --appium-url http://192.168.1.10:4723
pytest --app-app-package com.example.app --app-app-activity .MainActivity
pytest --app-session-mode clean-session
pytest --app-device-profile pixel7
pytest --app-video-policy failed
pytest --app-is-simulator
pytest --app-capabilities-json '{"autoGrantPermissions": true}'
pytest --app-manage-appium-server    # start Appium automatically
pytest --app-reporting-enabled       # write artifacts/appium-pytest-kit/summary.json

See DOCUMENTATION.md § Configuration for the full settings table.


Expanded waits

waiter.for_clickable(locator)                     # wait for element to be tappable
waiter.for_invisibility(locator)                  # wait for element to disappear
waiter.for_text_contains(locator, "partial text") # wait for text substring
waiter.for_text_equals(locator, "exact text")     # wait for exact text match
waiter.for_all_visible([loc1, loc2, loc3])        # wait for all elements to appear
waiter.for_any_visible([loc1, loc2])              # wait for first visible element

Expanded actions

actions.tap_if_present(locator)       # tap only if visible, returns bool
actions.clear(locator)                # clear a text field
actions.swipe(sx, sy, ex, ey)         # W3C Pointer swipe gesture
actions.scroll_down()                 # swipe up on screen center
actions.scroll_up()                   # swipe down on screen center

Extension hooks

Implement these in your conftest.py to customise behaviour without touching the framework:

# conftest.py

def pytest_appium_pytest_kit_capabilities(capabilities, settings):
    """Add extra capabilities before each driver session."""
    return {"autoGrantPermissions": True, "language": "en"}


def pytest_appium_pytest_kit_configure_settings(settings):
    """Replace settings at session start."""
    return settings.model_copy(update={"implicit_wait": 2.0})


def pytest_appium_pytest_kit_driver_created(driver, settings):
    """Run setup immediately after each driver is created."""
    driver.orientation = "PORTRAIT"

Project scaffolding

Generate a full project structure in one command:

appium-pytest-kit-init --framework --root my-project

Creates:

my-project/
├── data/devices.yaml          # device profiles
├── pages/
│   ├── base_page.py           # BasePage composition class
│   └── example_page.py        # starter page object
├── flows/                     # reusable multi-step flows
├── tests/
│   ├── android/test_smoke.py
│   └── ios/test_smoke.py
├── conftest.py
├── pytest.ini
└── .env.example

Public API

Top-level imports (stable):

from appium_pytest_kit import (
    AppiumPytestKitSettings,
    AppiumPytestKitError,
    ConfigurationError,
    DeviceResolutionError,
    LaunchValidationError,
    WaitTimeoutError,
    ActionError,
    DriverCreationError,
    DeviceInfo,
    DriverConfig,
    MobileActions,
    Waiter,
    build_driver_config,
    create_driver,
    load_settings,
    apply_cli_overrides,
)

Stable public modules (direct import):

  • appium_pytest_kit.settings
  • appium_pytest_kit.driver
  • appium_pytest_kit.waits
  • appium_pytest_kit.actions
  • appium_pytest_kit.errors
  • appium_pytest_kit.interfacesCapabilitiesAdapter protocol for custom adapters

Private/internal modules (no compatibility guarantee):

  • appium_pytest_kit._internal.*

Fixture lifecycle

flowchart TD
    A["pytest start"] --> B["load defaults + .env + env vars"]
    B --> C["apply --app-* CLI overrides"]
    C --> D["settings fixture (session)"]
    D --> E{"APP_MANAGE_APPIUM_SERVER"}
    E -->|"true"| F["start local Appium server"]
    E -->|"false"| G["use APP_APPIUM_URL"]
    F --> H["appium_server fixture"]
    G --> H
    H --> I{"session_mode"}
    I -->|"clean-session / debug"| J["_driver_shared (session)"]
    I -->|"clean"| K["driver per test"]
    J --> K
    K --> L["waiter / actions fixtures"]
    K --> M["test executes"]
    M --> N{"failed?"}
    N -->|"yes"| O["capture screenshot + page source"]
    N --> P["video stop (per policy)"]
    O --> P
    P --> Q["driver.quit() if clean mode"]
    Q --> R["optional report summary flush"]
    R --> S["optional server stop"]

Local development

pip install -e ".[dev]"
ruff check .
pytest -q
pytest --collect-only examples/basic/tests -q

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

appium_pytest_kit-0.1.0.tar.gz (23.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

appium_pytest_kit-0.1.0-py3-none-any.whl (26.0 kB view details)

Uploaded Python 3

File details

Details for the file appium_pytest_kit-0.1.0.tar.gz.

File metadata

  • Download URL: appium_pytest_kit-0.1.0.tar.gz
  • Upload date:
  • Size: 23.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for appium_pytest_kit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6909406d207dd614a06cc908a9fa8c1f4a645f7c916392fde79d3a91f95b73f2
MD5 d25b72d90cb6832d017e35fffe13407c
BLAKE2b-256 51a4b57326656f17540a2b6d93c6976871b9c311ccffeab04e89694af9838eda

See more details on using hashes here.

File details

Details for the file appium_pytest_kit-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for appium_pytest_kit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9f27643f975644800014f0b93fad4a62360c01414d1f21abac87b85774a3c12b
MD5 ad5b0473db62d3c265522de108c894f1
BLAKE2b-256 12ae673b4a0baf371a880b1cfea14e0819c36876a4115ca991fa69193c4938e0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page