m00nreport-pytest
M00N Report reporter for pytest - real-time test reporting to m00nreport.com or your self-hosted instance.
Installation
pip install m00nreport-pytest
That's it. The package registers itself as a pytest plugin via the standard pytest11
entry point, so pytest discovers and loads it automatically on every run once it is
installed - there is no conftest.py wiring, -p flag, or config block to add.
Quick start
Set two environment variables and run pytest normally:
export M00N_SERVER_URL=https://m00nreport.com # or your self-hosted URL
export M00N_API_KEY=m00n_xxxxxxxxxxxxx # your project API key
pytest
On Windows PowerShell:
$env:M00N_SERVER_URL = "https://m00nreport.com"
$env:M00N_API_KEY = "m00n_xxxxxxxxxxxxx"
pytest
If either variable is missing, the reporter stays inactive and pytest behaves exactly as it would without it installed - see Resilience.
Get your API key
- Log in to M00N Report
- Open your project settings
- Generate or copy the project API key
Configuration
Every option can be set three ways. Precedence, highest wins:
CLI flag > pytest.ini key > environment variable > default.
| CLI flag | ini key | Env var | Default | Description |
|---|---|---|---|---|
--m00n-server-url |
m00n_server_url |
M00N_SERVER_URL |
required | M00N Report server URL |
--m00n-api-key |
m00n_api_key |
M00N_API_KEY |
required | Project API key (m00n_...) |
--m00n-launch |
m00n_launch |
M00N_LAUNCH |
Run <date> |
Title for this run |
--m00n-tags |
m00n_tags |
M00N_TAGS |
[] |
Comma-separated tags |
--m00n-attributes |
m00n_attributes |
M00N_ATTRIBUTES |
{} |
JSON object of custom run attributes |
--m00n-debug |
m00n_debug |
M00N_DEBUG |
false |
Prints a [m00nreport] line to stdout for every HTTP retry attempt and any final exhaustion (run/start, test/complete, run/end, attachment uploads), plus the attach()-outside-an-active-test edge case; silent when off |
--m00n-disable |
- | - | false |
Force-disable the reporter for this invocation, even if a server url and api key are set. CLI-only - no ini or env equivalent by design. |
The reporter activates only once both server_url and api_key resolve to a value. If
exactly one of them is set, pytest prints a single
[m00nreport] serverUrl and apiKey are required. Reporter disabled. warning to stderr
and continues (this usually means a typo in an env var name); if neither is set, the
reporter is silently inactive. A malformed M00N_ATTRIBUTES JSON string is ignored
(the reporter falls back to {}) rather than crashing the run.
pytest.ini example:
[pytest]
m00n_server_url = https://m00nreport.com
m00n_api_key = m00n_xxxxxxxxxxxxx
m00n_launch = Nightly Regression
m00n_tags = smoke, api
Or entirely from the CLI:
pytest --m00n-server-url https://m00nreport.com --m00n-api-key m00n_xxxxxxxxxxxxx --m00n-tags smoke,api
Manual steps
Wrap any block of test code in step(...) to record it as a named, timed step. Steps
stream to the dashboard live and nest naturally:
from m00n_reporter import step
def test_login():
with step("open login page"):
...
with step("submit credentials"):
with step("fill form"):
...
with step("click submit"):
...
A step whose body raises is recorded failed with the exception captured; a step that
exits normally is recorded passed. Any step still open when the test finishes is
recorded skipped.
Attachments
from m00n_reporter import attach
def test_checkout():
attach("screenshot.png") # file path - name/type inferred
attach(b"raw bytes", name="notes.txt") # bytes payload - name is required
attach(trace_path, name="trace.zip", content_type="application/zip")
attach(path_or_bytes, name=None, content_type=None) accepts either a file path
(str / os.PathLike) or a bytes / bytearray payload. Byte payloads must supply
name; file paths default name to the basename and guess content_type from it.
Files under 10 MB upload as buffered multipart; larger files stream instead. Anything
over 200 MB is skipped with a single warning. Uploads run on a background thread pool
(up to 15 concurrent) and attach() returns immediately - it never blocks the test.
pytest waits up to 5 minutes for pending uploads to finish before exiting, so
attachments from fast-finishing tests are not silently dropped. Calling attach()
outside of an active test, or when the reporter is disabled, is a silent no-op.
Case linking and tags
import pytest
@pytest.mark.m00n(case_id=42, tags=["regression", "checkout"])
def test_completes_checkout():
...
case_id links the test attempt to an existing M00N Report test case; tags adds
per-test tags on top of any run-level tags from configuration.
Retries
The reporter supports pytest-rerunfailures out of the box - no extra wiring required:
pip install pytest-rerunfailures
pytest --reruns 3
Each attempt is reported as a distinct entry with a fresh testId and an incrementing
zero-based retry index (0, 1, 2, ...), so the dashboard shows the full attempt
history of a flaky test rather than only its final outcome.
CI auto-detection
The reporter inspects the environment and auto-populates run attributes (branch,
commit, pipeline, build_number, build_url, trigger, triggered_by,
ci_job_url (GitLab) where available) for these providers, checked in this order
(first match wins):
- GitHub Actions
- GitLab CI
- Jenkins
- Bitbucket Pipelines
- Azure DevOps
- CircleCI
- Travis CI
Detected attributes are merged with, and overridden by, anything you pass via
--m00n-attributes / m00n_attributes / M00N_ATTRIBUTES.
Resilience
The reporter follows the same "never fail the run" rule as every M00N Report integration:
- Every pytest hook is wrapped so a bug or a dead server degrades to a single
[m00nreport] ...warning on stderr - it never turns a green run red, and never raises a pytestINTERNALERROR. run/start,test/complete,run/end, and attachment uploads retry transient 5xx responses up to 3 attempts (1 initial + 2 retries) with exponential backoff (min(1.0 * 2^(attempt-1), 5.0)seconds, capped at 10s for streamed uploads). Permanent error codes (invalid API key, project not found, run attachment limit exceeded) are never retried.- A circuit breaker disables the reporter for the rest of the run after 5 consecutive
test/completecalls report a service failure, so a sustained outage does not keep retrying every remaining test.
Two deliberate improvements over the JS Playwright reporter:
- Smarter health probe. The startup probe checks
/api/ingest/healthfirst (falling back to/healthz) and requires a200response with a JSON body of{"ok": true}- not just any200. The probe is advisory only: it never disables the reporter whenrun/startitself already succeeded, so a proxy or firewall rule that blocks the health endpoint but not the real API cannot take reporting offline. - No dropped
test/startcalls. Fire-and-forget calls (test/startand step batches) run through the same retrying background worker as the awaited calls, so a single transient 5xx can no longer permanently drop a test's start record while its completion still lands.
Parallel execution (pytest-xdist)
Parallel runs with pytest-xdist (-n auto,
-n 4, distributed workers) are supported and report as a single run - no extra wiring:
pip install pytest-xdist
pytest -n 4
How it works (master/worker coordination, the same model Allure and ReportPortal use):
- The controller (master) process starts the run exactly once and shares its run id with every worker before the worker boots, then closes the run once at the end. Its final status comes from the controller-aggregated result, so a failure on any worker fails the whole run.
- Each worker inherits that shared run id, skips its own
run/start/health probe, and reports its slice of tests (test/start, steps,test/complete, attachments) against the shared run. Workers never sendrun/end.
The result is one launch on the dashboard with every worker's tests, retries (retry
0, 1, ... survive across workers), skips, steps, and attachments merged into it -
identical to a serial run, just faster.
Caveat: because the controller does not perform test collection under xdist (the workers
do), the run's up-front total test count is omitted for parallel runs; the count fills
in from the tests as they report. Serial (non-xdist) runs still send total as before.
Known limitations
- Teardown-phase failures are not reported. If a test's own body passes but a
fixture's teardown/finalizer raises afterward, that failure is not surfaced to M00N
Report - the test keeps the status it already reported at
calltime. This is a known limitation of this release, not a bug in your fixtures.
Conformance
This reporter targets the M00N Report ingest v2 contract (public OpenAPI document) and
is verified against the M00N Report reporter-conformance suite: both the normal and
fault-injection (injected transient 5xx) baselines pass, all 11 protocol invariants
green on the normal run and all 12 (the additional RETRIES_ON_FAILURE check) green
on the fault-injection run. Unlike the JS Playwright reporter (1.0.10), whose
test/start call is fire-and-forget with no retry, this reporter's fire-and-forget
calls run through a retrying background worker and test/complete waits for its
matching test/start to land before sending, so the fault-injection baseline stays
fully green instead of losing test/start records to a single transient 5xx.
License
MIT License. See LICENSE.
Metadata
Release files for m00nreport-pytest 1.0.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 | |
|---|---|---|---|
| m00nreport_pytest-1.0.0.tar.gz | 45.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| m00nreport_pytest-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 75.6 kB
Release files / m00nreport_pytest-1.0.0.tar.gz
| Download URL | m00nreport_pytest-1.0.0.tar.gz |
|---|---|
| Size | 45.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2c7fe2a1c30810725b1ef408fa70d62718ceb356862a746309509f317435561f
|
|
BLAKE2b-256 checksum How to use checksums |
6a9a399e66af22330b0aebeb64484e1d8f613fe4d69f8db625c494154ae48350
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / m00nreport_pytest-1.0.0-py3-none-any.whl
| Download URL | m00nreport_pytest-1.0.0-py3-none-any.whl |
|---|---|
| Size | 30.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f4894b20e256beafed1351b9da41f7684a7aec54478379c04278fcd1e81f0e04
|
|
BLAKE2b-256 checksum How to use checksums |
3869a57295368eacf11a619414295745309641adcdca97342cbe7811026bbf41
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|