qamule-pytest
qamule-pytest provides reusable Android device and artifact fixtures for
pytest-based automation.
Devices are connected through uiautomator2 and reused for the whole test
session. Artifact directories are created only when a test or plugin requests
them.
Install
pip install qamule-pytest
Use Devices
Without configuration, request the default d fixture:
def test_smoke(d):
assert d.serial is not None
Register named devices by repeating --device NAME:SERIAL:
pytest --device phone:emulator-5554 --device tablet:emulator-5556
Set one uiautomator2 server port for all configured devices when needed:
pytest --device phone:emulator-5554 --device-port 26831
def test_on_phone(phone):
assert phone.serial == "emulator-5554"
Device names must be non-keyword Python identifiers and must not use a fixture
name reserved by qamule-pytest. When --device-port is omitted, the server
port is not passed to uiautomator2 and its default is used.
qamule-pytest currently supports single-process pytest execution. It rejects
active pytest-xdist runs with a usage error; run without -n or
--numprocesses.
Use device_manager.get(name) for lazy lookup. The devices fixture connects
and returns all configured devices.
Artifact Directories
Use the fixtures exposed by the plugin:
def test_capture(testcase_device_artifacts_dir):
screenshot = testcase_device_artifacts_dir("phone") / "screen.jpg"
screenshot.write_bytes(b"example")
Artifacts use the following layout:
pytest-artifacts/<session-id>/<sequence>-<testcase>/<device-name>/
When a test call fails, each device already connected during the pytest session is captured automatically:
<device-name>/failure.jpg
<device-name>/hierarchy.xml
Failure capture does not connect unused devices. Screenshot and hierarchy
capture are independent best-effort operations, so capture errors never replace
the original test failure. Expected xfail results are not captured.
Available fixtures:
session: metadata and paths for the current pytest run.artifacts_dir: artifact root for the current run.testcase: metadata and paths for the current test execution.testcase_artifacts_dir: artifact directory for the current test.testcase_device_artifacts_dir(name): artifact directory for one device.
Directories are created lazily. Requesting session alone does not write an
artifact directory.
Plugins that cannot request fixtures from a hook can use get_session(config),
get_testcase(item), or get_testcase_device_dir(item, name) from
qamule_pytest.features.artifacts.plugin.
Session Reports
Every pytest run prints its QAMule session ID and writes live report data under:
pytest-sessions/<session-id>/events.jsonl
pytest-sessions/<session-id>/state.json
events.jsonl is an append-only stream containing session, collection, test
lifecycle, phase result, duration, and failure traceback events. state.json is
an atomically updated snapshot containing the pytest process ID, session state,
timestamps, exit status, and result totals.
Inspect a session from the directory where pytest started:
uv run qamule report status <session-id>
uv run qamule report show <session-id> 'tests/test_login.py::test_login'
Serve the realtime report locally:
uv run qamule report serve <session-id>
uv run qamule report serve ./pytest-sessions/<session-id>
uv run qamule report serve <session-id> --host 127.0.0.1 --port 26826 -q
The report receives incremental updates over Server-Sent Events. Only the
selected case's evidence is rendered, report JSON contains image metadata only,
and the browser requests each image only when it is near the visible area.
report serve accepts either a session ID or the absolute or relative path to
its pytest-sessions/<session-id> directory. Use --root PATH with a session
ID when another report command runs outside the directory where pytest started.
This package intentionally does not export static report bundles.
Interactive Pauses
Use the checkpoint fixture to stop a test until an external decision arrives:
def test_login(checkpoint):
decision = checkpoint(
"confirm the login screen",
images=["screens/login.jpg"],
)
assert decision.result is True
Pause automatically after failed test calls with:
uv run pytest --pause-on-failure
Every checkpoint and failure pause times out after 600 seconds by default. Set
a different positive duration for the whole pytest run with
--pause-timeout SECONDS. A timed-out checkpoint returns a decision with
result=None, has_result=False, and timed_out=True. A timed-out failure
pause releases pytest so the original test failure can finish.
To exercise checkpoint-dependent tests without external interaction, mock every checkpoint result for the pytest run:
uv run pytest --checkpoint-mock-result true
uv run pytest --checkpoint-mock-result false
The checkpoint still publishes its paused event and artifacts, then
immediately resumes with the selected boolean result. While this option is
enabled, checkpoint watch instructions are not printed and the resume message
is mock via --checkpoint-mock-result.
While pytest is paused, use its printed session ID from another terminal:
uv run qamule watch <session-id>
uv run qamule resume <session-id> <pause-id> \
--result true \
--message "login screen approved"
Checkpoint resumes require --result true, --result false, or --result none. Failure pauses do not require a result. Attach images by repeating
--image PATH. Common image formats such as JPG, PNG, GIF, and WebP are
supported when Python can identify their MIME type. JPG is recommended and is
the format used for automatic failure screenshots.
Pause state is stored under
pytest-sessions/<session-id>/pauses/<pause-id>/. Image bytes are deduplicated
under pytest-sessions/<session-id>/blobs/<sha256>; status and resume files
contain only metadata and hashes. watch also returns the final session state
if pytest finishes or reports process_exited if the pytest process disappears.
Resume and timeout compete for one atomically published resolution.json, so a
resume accepted at the deadline cannot later be overwritten by a timeout.
License
qamule-pytest is released under the MIT License.
Development
From the workspace root:
uv sync --package qamule-pytest
uv run pytest packages/qamule-pytest/tests -q
Metadata
Release files for qamule-pytest 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| qamule_pytest-0.1.0.tar.gz | 70.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qamule_pytest-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 134.1 kB
Release files / qamule_pytest-0.1.0.tar.gz
| Download URL | qamule_pytest-0.1.0.tar.gz |
|---|---|
| Size | 70.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
016fb195c8e967f7ab7d1edecd94a7dd657530a67fc56a9041809b5fdcf003db
|
|
BLAKE2b-256 checksum How to use checksums |
b353854b9cf6eab6a27cfa6471baec686b8f30f243ab8015404bbfa76ffb4a08
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / qamule_pytest-0.1.0-py3-none-any.whl
| Download URL | qamule_pytest-0.1.0-py3-none-any.whl |
|---|---|
| Size | 64.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8032c974eea3c7205b638b24b0b6833130002d482ca21d8233a65b874a808cbe
|
|
BLAKE2b-256 checksum How to use checksums |
37453c745d8971d204c6b935b5123c2540fc81b9221c67e07e6a1df05de405bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for qamule-pytest 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.