💡 pytest-better-report
A pytest plugin that writes a structured report of every run: a JSON file per test, a JSON file for the run with CI metadata (repo, PR/MR, pipeline, commit), and an optional Markdown summary. Includes a CLI to compare two runs.
📦 Installation
uv add pytest-better-report
🚀 Features
- ✅ Structured JSON —
test_results.json(one entry per test) andexecution_results.json(the run) - ✅ CI traceability — repo name, PR / MR number, pipeline number and commit in the run report
- ✅ Markdown summary — a table of every test with status, duration and message
- ✅ Structured failure data — raise with a JSON object as the message to record it as data
- ✅ Compare two runs —
better_report_comparelists missing tests and status differences - ✅ Plays well with other plugins — statuses come from pytest's final reports, whatever the plugin order
- ✅ Zero footprint without the flag — nothing is registered, no hook runs
🛠️ How to Use
pytest --better-report --md-report --pr-number=123 --commit="$GIT_SHA"
Files are written to results_output/ (or <output-dir>/results_output/):
| File | Content |
|---|---|
test_results.json |
One entry per test: name, file, class, parameters, markers, status, times, exception, user properties |
execution_results.json |
The run: status, start / end / duration, CI metadata, environment, optionally the pytest command |
test_report.md |
With --md-report: a table of all tests and a summary by status |
⚙️ Flags
| Flag | Description |
|---|---|
--better-report |
Enable the report |
--output-dir=DIR |
Write the files to DIR/results_output/ |
--md-report |
Also write test_report.md |
--traceback |
Add the crash location and traceback frames to each exception |
--repo-name, --pr-number, --mr-number, --pipeline-number, --commit |
CI metadata for execution_results.json |
--pytest-command |
Record the pytest command line in execution_results.json |
--pytest-xfail-strict |
Treat an unexpected pass (XPASS) as a failure of the run |
--result-each-test |
Log each test's result after it runs |
--log-collected-tests |
Log every collected test at the start (works without --better-report too) |
📊 Statuses
Each test gets one status in test_results.json.
The run as a whole gets execution_info.execution_status in execution_results.json: failed if at least one
test has a status marked "Yes" in the last column, otherwise passed.
This is the report's own verdict on the run, written into the file. It is separate from pytest's exit code, which this plugin doesn't change.
| Test status | Meaning | Makes execution_status = failed |
|---|---|---|
passed |
Passed | No |
failed |
Failed or errored in setup, call or teardown | Yes |
skipped |
Skipped (marker, fixture, or a plugin that converted the outcome) | No |
xfailed |
Expected failure | No |
xpassed |
Unexpected pass | Only with --pytest-xfail-strict |
collected |
Collected but not run (e.g. the run stopped early) | No |
▶️ Usage Examples
Record structured failure data
import json
def test_api():
response = call_api()
if response.status != 200:
raise AssertionError(json.dumps({"status": response.status, "body": response.text}))
The exception is recorded as {"status": ..., "body": ...} instead of plain text.
Compare two runs
better_report_compare -a main/test_results.json -b branch/test_results.json --fail-on-diff
Lists tests missing from either side and tests whose status changed; exits non-zero with --fail-on-diff when
there are differences. --test-file-name limits the comparison to one test file.
🔁 Migrating from pytest-plugins
This plugin was previously better_report in pytest-plugins. Flags, file names and the JSON schema are unchanged.
Both register the same options, so don't install them together until you're on a pytest-plugins release without it.
Differences you may notice:
- A test that errors in teardown is now
failed(it was reported as passed). - The
failed-skippedstatus no longer exists; converted failures areskipped, with the reason intest_user_properties. - The logger is named
pytest_better_report. --add-parameterswas removed. Parameters are always recorded intest_parameters; in ELK / Kibana query them astest_parameters.<name>instead of<name>. Passing the flag now fails with "unrecognized arguments".
🤝 Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-addition - Make your changes
- Commit your changes:
git commit -m 'Add my addition' - Push to the branch:
git push origin feature/my-addition - Open a Pull Request
Additions that promote clean, productive, and maintainable development are always welcome.
📄 License
MIT License — see LICENSE for details.
🙏 Thanks
Thanks for exploring this repository!
Happy coding!
Metadata
Release files for pytest-better-report 1.0.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 | Provenance | Actions |
|---|---|---|---|---|
| pytest_better_report-1.0.1.tar.gz | 15.9 kB | Without provenance | Details for pytest_better_report-1.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pytest_better_report-1.0.1-py3-none-any.whl | Python 3 | none | any | Details for pytest_better_report-1.0.1-py3-none-any.whl |
Total release size: 32.6 kB
Release files / pytest_better_report-1.0.1.tar.gz
| Download URL | pytest_better_report-1.0.1.tar.gz |
|---|---|
| Size | 15.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
87bda5c76cfe0a2af8bbddc81a9346647c89a8cd1b86d8321015cc38a5ad21d4
|
|
BLAKE2b-256 checksum How to use checksums |
6d5d0456345f355c82e0e4d074b995087e869700fc0944d2103fcf6ac7abb22a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Release files / pytest_better_report-1.0.1-py3-none-any.whl
| Download URL | pytest_better_report-1.0.1-py3-none-any.whl |
|---|---|
| Size | 16.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
483a6bfc3ec904b392bfc0e5545690e922cd2d43acdcfcfbbbbdefa04028906b
|
|
BLAKE2b-256 checksum How to use checksums |
2146b8dc40102f0725529f8852a4f9b257c728b3d5abe965b474a4b7f9d8c9ba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Security for pytest-better-report 1.0.1
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.