Skip to main content

m00nreport-pytest

PyPI CI python license

Official pytest plugin for M00N Report, an AI-native test management platform. Results, steps, attachments and retries land in M00N Report while the suite is still running, and each test can carry the manual test case it covers. A reporting problem is never a test failure: every hook is guarded, and the worst case is a single [m00nreport] warning on stderr.

Requirements

Python 3.9 or newer, pytest 7.0 or newer, and pluggy 1.2 or newer.

pip installs pluggy for you, so the floor is stated only because it bites when a lockfile gets it wrong: pytest 7 alone permits pluggy 0.12, and this plugin uses @pytest.hookimpl(wrapper=True), which is pluggy 1.2 API and raises at import below it. A pytest11 plugin that raises at import takes down every pytest invocation in that environment, including suites that have nothing to do with reporting.

The Playwright layer additionally needs pytest-playwright, and retries need pytest-rerunfailures. Both are optional.

Installation

pip install m00nreport-pytest

# with the Playwright layer
pip install "m00nreport-pytest[playwright]"

The package registers itself through the standard pytest11 entry point, so pytest loads it on every run once it is installed. There is no conftest.py wiring, no -p flag, no config block.

Quick Start

1. Get your API key

In M00N Report, open Project Settings -> API Keys and copy the key. It starts with m00n_ followed by 48 hex characters, and it identifies both the organization and the project.

Keep it out of the repository. In CI it belongs in a secret; locally, in an environment variable.

2. Point the plugin at your project

export M00N_SERVER_URL=https://m00nreport.com   # or your own instance, root origin, no /api, no trailing slash
export M00N_API_KEY=m00n_...

PowerShell:

$env:M00N_SERVER_URL = "https://m00nreport.com"
$env:M00N_API_KEY = "m00n_..."

Everything except the key can live in the repository instead:

# pytest.ini
[pytest]
m00n_launch = Nightly regression
m00n_tags = nightly,regression

The same keys work in setup.cfg under [tool:pytest] and in pyproject.toml under [tool.pytest.ini_options].

3. Run your tests

pytest
[m00nreport] Run started: 9f3c1a7e-4d21-4b8a-9e77-2c5b1d0a6f84

--------------------------------- M00N Report ---------------------------------
  Run ID:    9f3c1a7e-4d21-4b8a-9e77-2c5b1d0a6f84
  Tests:     3/3 reported
  Failed:    1

The run id is printed the moment the run starts, and again in the summary at the end. Both lines carry the same id, which is the run in the project's Launches list, filling in as tests finish. No [m00nreport] line at all means the plugin never activated - see Troubleshooting.

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 Your M00N Report 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. Malformed JSON is ignored rather than crashing the run.
M00N_ATTRIBUTE_<NAME> One attribute per variable, which is easier to set in a CI job than a JSON object quoted inside a YAML value. M00N_ATTRIBUTE_BRANCH=main becomes the attribute branch, and wins over the same key in M00N_ATTRIBUTES.
--m00n-debug m00n_debug M00N_DEBUG false Print a [m00nreport] line on stdout for every HTTP retry and any final exhaustion. Silent when off.
--m00n-disable false Turn the plugin off for this invocation even when a URL and key are set. CLI only, by design.

The plugin activates only once both the URL and the key resolve to a value.

A discovery pass creates no launch. --collect-only, --setup-only and --setup-plan run no tests, so the plugin stays out of the way entirely. Without that rule an IDE refreshing its test tree on every save fills the Launches list with empty runs.

Steps

from m00n_reporter import step

def test_checkout():
    with step("Add item to cart"):
        cart.add(sku="A-1")

    with step("Pay"):
        with step("Enter card"):
            checkout.enter_card(EXPIRED_CARD)
        checkout.submit()

Steps nest, and their timings are recorded. A step still open when the test fails is reported as skipped, so the step list shows how far the test got.

Attachments

from m00n_reporter import attach

def test_api_contract():
    response = client.get("/api/cart")

    attach(response.content, name="cart.json", content_type="application/json")
    attach("artifacts/screenshot.png")           # a path works too

Bytes need a name; for a path it defaults to the basename. Content type is guessed from the name when you do not pass one.

A file over 200MB is skipped with one warning (recount: grep -n 'MAX_ATTACHMENT_SIZE =' src/m00n_reporter/attachments.py). When a run reaches your plan's attachment quota the plugin warns run attachment limit reached (N/M MB used) and drops the rest of that run's uploads, including ones already queued. Neither ever fails the test.

Linking Tests to Cases

A m00n marker attaches the result to a manual test case, which is what turns a run into coverage rather than a list of green ticks.

import pytest

@pytest.mark.m00n(case_id=42, tags=["payments", "smoke"])
def test_checkout_rejects_expired_card():
    ...

case_id is the case's internal id, not the TC-42 number shown in the UI. Open the case in M00N Report and take the id from the address bar. Tags here are per test, on top of the run-level --m00n-tags.

Playwright Integration

With pytest-playwright installed, the plugin instruments the page fixture on its own. There is nothing to enable and no base class to inherit.

def test_login(page):
    page.goto("https://example.com/login")   # each of these becomes a step
    page.fill("#email", "user@example.com")
    page.click("button[type=submit]")

Direct page calls - goto, click, fill, press, wait_for_selector and the rest of the Page API - become steps carrying their selector and timing, and a screenshot is captured and attached when a test fails.

Locator calls do not become steps. page.get_by_role("button").click() goes through a Locator, which the plugin does not wrap; put those inside with step(...) if you want them in the report.

Video and trace files are uploaded too, on failing tests only, and only if you asked pytest-playwright to record them - both are off by default:

pytest --video=retain-on-failure --tracing=retain-on-failure

Retries

With pytest-rerunfailures, every attempt is reported, not only the final verdict, so a flaky test is visible as a flaky test rather than as a pass.

pytest --reruns 2

The summary counts attempts, so one test that passed on its third try reads as Tests: 3/3 reported.

Parallel Runs

Under pytest-xdist the whole run is a single launch, with no configuration: the controller starts it, every worker reports its own slice into it, and the controller closes it. Only the controller prints the run id and the summary, so -n 8 does not print eight of them.

pytest -n auto

CI Auto-Detection

Running in GitHub Actions, GitLab CI, Jenkins, Bitbucket Pipelines, Azure DevOps, CircleCI or Travis CI needs no configuration: branch, commit, pipeline, build number, build URL and who triggered it are detected and attached to the run. Anything you set through --m00n-attributes or M00N_ATTRIBUTE_<NAME> overrides a detected value.

# .github/workflows/tests.yml
name: Tests
on: push

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install -r requirements.txt
      - run: pytest
        env:
          M00N_SERVER_URL: https://m00nreport.com
          M00N_API_KEY: ${{ secrets.M00N_API_KEY }}

What Gets Sent

To your server URL over HTTPS: test node ids and file paths, step names and timings, outcomes, error messages and stack traces, the CI attributes above, and whatever you pass to attach().

A stack trace is pytest's own long representation of the failure - the same text pytest prints in its FAILURES section - so it carries the source lines around the failing assertion.

Not sent: environment variables beyond the CI attributes above. The API key is never printed, including under --m00n-debug.

Troubleshooting

Warnings go to stderr, prefixed [m00nreport]. The run id line, the summary and every --m00n-debug line go to stdout, so redirecting only stderr to a file captures the warnings and loses the run id. The suite stays green in every case below.

Message or symptom Cause
serverUrl and apiKey are required. Reporter disabled. Exactly one of the two resolved, which usually means a typo in an environment variable name.
Server unavailable (<url>). Reporter disabled - tests will continue without reporting. Nothing answered at that URL. Check for a typo, an /api suffix, or a host reachable only over VPN.
Unauthorized. Reporter disabled. The key was rejected: unknown, revoked, or belonging to an inactive organization. The wording is the server's own, so another rejection reads differently, but Reporter disabled. always means a permanent error - attempted once, never retried.
HTTP 503. Reporter disabled for this run. The server answered its health check but could not start the run, after three attempts. Most often a deploy in progress.
Multiple service failures detected. Disabling reporter for remaining tests. Five consecutive service failures tripped the breaker. The run is reported up to that point and no further.
No [m00nreport] line and no run in the app Neither the URL nor the key resolved, so the plugin stayed silently inactive. This is the expected state on a developer machine with nothing configured. In CI it is most often a forked pull request: GitHub does not expose secrets to them, so secrets.M00N_API_KEY is an empty string.
A run appears, but with no steps from Playwright pytest-playwright is not installed, so the layer stayed off. Install the [playwright] extra.
Retries appear as a single result pytest-rerunfailures is not installed, so pytest never reran the test.

--m00n-debug prints every HTTP retry and any final exhaustion, which distinguishes "never tried" from "tried and could not reach the server".

Known Limitations

  • Teardown-phase failures are not reported. If the test body passes but a fixture teardown raises afterwards, the test keeps the status it already reported at call time.
  • Runs are not linked to releases. The plugin reports a launch; attaching launches to a release is done in the app or over MCP.

Support

License

MIT License. See LICENSE.

Metadata

Release files for m00nreport-pytest 1.1.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.1.0
File Size Uploaded
m00nreport_pytest-1.1.0.tar.gz 59.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for m00nreport-pytest 1.1.0
File Interpreter ABI Platform
m00nreport_pytest-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 93.9 kB

Release files / m00nreport_pytest-1.1.0.tar.gz

Download URL m00nreport_pytest-1.1.0.tar.gz
Size 59.7 kB
Tags Source
SHA-256 checksum
How to use checksums
92ccc23e26539d6df2f3bee333b9af0adc46ba583551a80d6cdf8b82958e74d2
BLAKE2b-256 checksum
How to use checksums
74790173899cfd2dff8c51df2d60ae61ffd74c895fa0c731e2e6494612e2400a
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.1.0-py3-none-any.whl

Download URL m00nreport_pytest-1.1.0-py3-none-any.whl
Size 34.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a02e6ac462f31a33079e0070aa14fceeafd365628f3b46ba79854c7e65747e51
BLAKE2b-256 checksum
How to use checksums
c8121b93baacc29b567fc071560785848666d82e9b5e8923301806fbf59c2d4e
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

This release

1.1.0 This release

2 release files

1.0.0

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