Skip to main content

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

  1. Log in to M00N Report
  2. Open your project settings
  3. 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):

  1. GitHub Actions
  2. GitLab CI
  3. Jenkins
  4. Bitbucket Pipelines
  5. Azure DevOps
  6. CircleCI
  7. 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 pytest INTERNALERROR.
  • 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/complete calls report a service failure, so a sustained outage does not keep retrying every remaining test.

Two deliberate improvements over the JS Playwright reporter:

  1. Smarter health probe. The startup probe checks /api/ingest/health first (falling back to /healthz) and requires a 200 response with a JSON body of {"ok": true} - not just any 200. The probe is advisory only: it never disables the reporter when run/start itself already succeeded, so a proxy or firewall rule that blocks the health endpoint but not the real API cannot take reporting offline.
  2. No dropped test/start calls. Fire-and-forget calls (test/start and 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 send run/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 call time. 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)

Source distribution for m00nreport-pytest 1.0.0
File Size Uploaded
m00nreport_pytest-1.0.0.tar.gz 45.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for m00nreport-pytest 1.0.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

1.1.0

2 release files

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page