pytest-appium-scheduler
pytest-appium-scheduler is a pytest plugin for Appium test execution with:
- device-aware scheduling
pytest-xdistparallel executionfunctionandsessiondriver lifecycle control- per-test device targeting via markers
- debug and trace logs for worker/device visibility
The package is published as pytest-appium-scheduler. After publishing, both of these forms work with pip:
pip install pytest-appium-scheduler
Features
distributedmode: spread Appium tests across available devicesallmode: run each Appium test on every matching device@pytest.mark.device(...)and@pytest.mark.devices(...)- shared device pool with worker-safe leasing
driveranddevicefixtures- extensible hooks:
pytest_appium_create_driverpytest_appium_modify_caps
- compact debug output via
--appium-debug - verbose lifecycle tracing via
--appium-trace
Installation
pip install pytest_appium_scheduler
This installs:
pytestpytest-xdistPyYAMLappium-python-client
Quick Start
Create a device config, for example from device.example.yaml:
devices:
- name: emulator-5554
url: http://127.0.0.1:4723
caps:
platformName: Android
automationName: UiAutomator2
udid: emulator-5554
systemPort: 8201
appPackage: com.android.settings
appActivity: .Settings
- name: emulator-5556
url: http://127.0.0.1:4723
caps:
platformName: Android
automationName: UiAutomator2
udid: emulator-5556
systemPort: 8202
appPackage: com.android.settings
appActivity: .Settings
Write tests:
import pytest
def test_open_app(driver):
assert driver.session_id
def test_device_fixture(device):
assert device.name
@pytest.mark.device("emulator-5556")
def test_only_on_specific_device(driver):
assert driver.session_id
@pytest.mark.device(platform="Android")
def test_only_android(driver):
assert driver.session_id
class TestNoDevice:
def test_plain_pytest_case(self):
assert True
Run in distributed mode:
pytest tests \
--appium-config=device.yaml \
--appium-mode=distributed \
-n 2
Run in all mode:
pytest tests \
--appium-config=device.yaml \
--appium-mode=all \
-n 2
Scheduling Modes
distributed
Use devices as a shared execution pool.
- Appium tests are scheduled onto matching devices
- when workers are more than devices, extra workers can stay idle
- non-Appium tests can still run on idle workers
- when workers are fewer than devices, workers will rotate across devices instead of permanently starving one
all
Run every matching Appium test on every matching device.
- each device gets its own execution stream
- workers do not compete for the same device at the same time
- non-Appium tests can still run on idle workers
Fixtures
driver
Provides an Appium driver.
device
Provides the selected device model:
def test_device(device):
assert device.name
assert device.url
assert device.caps
Markers
Limit a test to one or more devices:
@pytest.mark.device("pixel_7")
def test_only_pixel(driver):
...
@pytest.mark.devices(["pixel_7", "iphone_14"])
def test_specific_devices(driver):
...
@pytest.mark.device(platform="iOS")
def test_only_ios(driver):
...
Behavior:
- unmarked tests: all configured devices are eligible
- unknown device marker: skipped with warning
- unmatched filter: skipped with warning
CLI Options
--appium-mode=distributed|all
--appium-config=PATH
--appium-device=NAME
--appium-driver-scope=function|session
--appium-retry-session=1
--appium-debug
--appium-trace
Useful examples
Select one device only:
pytest tests \
--appium-config=device.yaml \
--appium-device=huawei_p60
Reuse one driver per device session:
pytest tests \
--appium-config=device.yaml \
--appium-driver-scope=session
Show compact scheduling logs:
pytest tests \
--appium-config=device.yaml \
--appium-mode=distributed \
--appium-debug \
-n 2 -q -s
Show verbose lifecycle trace:
pytest tests \
--appium-config=device.yaml \
--appium-mode=distributed \
--appium-trace \
-n 2 -q -s
Custom Hooks
Modify capabilities
def pytest_appium_modify_caps(caps):
updated = dict(caps)
updated["newCommandTimeout"] = 120
return updated
Create your own driver
pytest_appium_modify_caps is applied before pytest_appium_create_driver, so your custom driver hook receives the updated capabilities.
def pytest_appium_create_driver(device):
from appium import webdriver
from appium.options.common.base import AppiumOptions
options = AppiumOptions()
options.load_capabilities(device.caps)
return webdriver.Remote(device.normalized_url(), options=options)
Debug Output
With --appium-debug:
[appium] gw0 target=huawei_p60 device=huawei_p60 PASSED tests/test_real_device.py::test_open_app session-123
With --appium-trace:
[pytest-appium-scheduler] action=device-acquire worker=gw0 target=huawei_p60 device=huawei_p60 details=nodeid=...
Development
Create a local environment:
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
Run tests:
.venv/bin/python -m pytest tests/test_plugin.py -q
Build distributions:
.venv/bin/python -m pip install build
.venv/bin/python -m build
License
MIT. See LICENSE.
Release files for pytest-appium-scheduler 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pytest_appium_scheduler-0.1.1.tar.gz | 22.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytest_appium_scheduler-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:43.5 kB
Release files / pytest_appium_scheduler-0.1.1.tar.gz
| Download URL | pytest_appium_scheduler-0.1.1.tar.gz |
|---|---|
| Size | 22.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9270c32a75be3516fb6d64b2d6e2e44f8bcc67b49200cff130b90d34b9ec01de
|
|
BLAKE2b-256 checksum How to use checksums |
de5d3ec782ca31f4b32cdf87db8748de0b43c0cf9dd344cd8bc61bfee1c2886b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / pytest_appium_scheduler-0.1.1-py3-none-any.whl
| Download URL | pytest_appium_scheduler-0.1.1-py3-none-any.whl |
|---|---|
| Size | 21.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
300b1f9d7d8a7c285eda45fcd4c7e115cc8238c2313ba4e78a153988e8c9a718
|
|
BLAKE2b-256 checksum How to use checksums |
1e8025ec626ba1d0536be5e49bbe3a304719691ca87f062e53db7e8d64413c6e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|