pytest-html-reporter
The pytest plugin that generates the report.
pytest-html-reporter turns a pytest run into a single, self-contained HTML page — no server, no build step, no assets folder. Overview, analytics, per-test steps, screenshots, captured logs, coverage and a full archive of every build before this one, in light or dark.
Nothing to register, nothing to import. Add the package, run pytest, and the report is
waiting in report/ — and on a local run it opens by itself.
One package. It registers itself with pytest through an entry point.
No flags needed. Add --html-report only if you want a different path, filename or title.
A single HTML file you can email, attach to a ticket, or publish to Pages as-is.
$ pip3 install pytest-html-reporter
$ pytest tests/
# -> ./pytest_html_report.html
$ pytest tests/ --html-report=./report/report.html --title='PYTEST REPORT'
# strftime placeholders give each run a folder of its own
$ pytest tests/ --html-report=./reports/%Y%m%d/report_%H%M.html
[pytest]
addopts = --html-report=./report --title='Nightly'
report_screenshots = failed
Three ways to get a report out of pytest, across sixty-three things a report is asked to do. Where a tool can do something only with a flag or a line of your own code, the cell says which one.
| Section | Capability | pytest-html | pytest-html-reporter this project | allure-pytest |
|---|---|---|---|---|
| Ease of setup | The pytest run writes the finished report | results first, then allure generate |
||
| A report with no flags at all | needs --html=path |
a plain pytest run writes one |
needs --alluredir, then a render |
|
| Nothing to install beyond Python | Allure 2 needs Java; Allure 3 needs Node.js | |||
| It opens itself when the run ends | and stays quiet on a build agent | through allure serve |
||
| Report portability | One self-contained HTML file | with --self-contained-html; otherwise an assets/ folder |
the report is self-contained; screenshot files may also be kept beside it | Allure 3: --single-file / singleFile: true; multi-file by default |
| No second tool to render it | the allure CLI does the rendering |
|||
| Mail it, or attach it to a ticket | once it is self-contained | one file, always | with Allure 3 single-file mode | |
| Deep links & shareability | A deep link to one test | built from the node id, not the row's position | a URL per test case | |
| Copy that link from the row itself | beside the failure | |||
| Dashboard UX | Status counts and run duration | |||
| Search, sort and filter the results | ||||
| One click for “only the failures” | status checkboxes | the count pills are the filter | ||
| Export the table to CSV, Excel or print | five exports, from the table itself | CSV export; not Excel or print | ||
| A breakdown per suite | the Suites tab | |||
| Theme | Light and dark themes | no built-in toggle; custom CSS with --css |
follows the machine, remembers the choice | Allure 3 has configurable themes, via --theme |
| Failure triage | Failures grouped by the exception behind them | biggest group first, tests named | categories instead; Allure 2 also ships Product and Test defects, and custom rules | |
| Copy the error, or the rerun command | quoted for your shell | |||
| A cut message that says it was cut | and the exports still carry the ellipsis | |||
| Screenshots | Taken with no hook or fixture of your own | you take and attach them | a failing Selenium or Playwright test is photographed | you take and attach them |
| A gallery of every picture the run took | inline on the row | a tab of its own, and a thumbnail on the row | per test, as attachments | |
| Logs | Captured stdout, stderr and logging | as attachments | ||
| How many lines each test produced | a column, opening the output section by section | |||
| API debugging | Attach request and response payloads | through the extras hook | four helpers | through allure.attach |
| The curl that repeats the call, credentials blanked | both bodies, both header sets | |||
| A structured request and response viewer | an attachment, or code of your own | an API Logs tab: headers, bodies, status, timing and the cURL | not in allure-pytest; HTTP-exchange viewers come from integrations such as Axios or Fetch |
|
| Security | Sensitive API values redacted for you | no built-in API redaction | authorization, cookies, tokens, secrets, passwords and API keys | not automatic in allure-pytest; some HTTP-client integrations do redact |
| Step diagnostics | Named, timed, nested steps | step(), as a block or a decorator |
steps are part of its model | |
| A tree even when you name nothing | set up, body and tear down, each timed | fixtures arrive as set up and tear down | ||
| The failing step carries the message and the picture | ||||
| Attachments land on the step that was open | a paperclip on the step | |||
| Async and concurrent steps keep their timing and nesting | no step model | async with step(), @step on an async def, concurrent tasks stay siblings and attachments stay with the right task |
its steps follow the sync and thread context | |
| BDD & markers | Gherkin scenarios with no changes to the test | the scenario and its Gherkin steps, captured automatically | the separate Allure pytest-bdd integration | |
| Markers shown with the scope they came from | module, class or added at runtime | its own decorators instead | ||
| Analytics | A stability score for the suite | 0–100, charged for flipping as well as failing | ||
| Flaky tests, kept apart from always-failing | from history and retries | |||
| Where the time goes | tests bucketed by duration | duration charts | ||
| What changed since the last build | newly failing, newly fixed, new, no longer run | new, fixed, regressed and malfunctioned, once history is configured | ||
| Ownership & priority | Owner-based analytics | who owns what: test share, mean pass rate, failing now, flaky tests and time per owner | Allure 3 can group analytics by owner, but not as one roll-up panel | |
| Severity-based failure analysis | how much it matters: blocker down to trivial, plus unrated, with failing counts | a built-in severity and status chart | ||
| Historical analysis | History across builds | reports one run | archived in the page itself | through the Allure toolchain |
| Pass-rate and coverage trends | pass rate | |||
| The failure delta since the last build | ▲ +3 failures, and which way it moved | trend charts instead | ||
| Retention by count, by age or by date | three limits, and they intersect | |||
| Test coverage | A Coverage tab when pytest-cov ran | ring, per-file rows, trend | ||
| Coverage measured in an earlier job | Cobertura, coverage.json or a data file |
|||
| xdist | One report from a pytest-xdist run | merged in collection order | ||
| Reruns and retries | evidence survives a green retry | |||
| What each attempt failed with | the Rerun count opens the trail |
|||
| Multi-machine sharding | Shards merged into a single build | --report-shard-merge |
copy the result dirs together | |
| The merge runs after the shards finish | a command of its own, offline | allure generate over the lot |
||
| JUnit XML | Written by the same run | pytest's own --junitxml |
--report-junit, from the same records |
pytest's own --junitxml |
| Traceability metadata written into each testcase | record_property by hand |
owner, severity and configured TMS or issue markers become <property> elements |
Allure labels don't become pytest JUnit properties | |
| Machine-readable output | Machine-readable results beside the report | not from pytest-html itself | output.json, written for every build |
the raw Allure JSON result files |
| Traceability / TMS | Issue ids turned into links | --report-link-pattern |
--allure-link-pattern |
|
| Links of your own in the side nav | --report-link |
|||
| Enterprise metadata | Environment and build details | its environment table | --environment and --build-info |
environment.properties |
| Owner / team labels | no built-in owner model | @pytest.mark.owner("team"), several owners on one test, and the badge can link out |
owner is a supported Allure label |
|
| Severity labels | no built-in severity model | blocker, critical, normal, minor and trivial; unrated stays unrated | the same five severity levels | |
| Extensibility / hooks | A Python API for attachments and steps | extras gives rich attachments and content, but no step API |
six functions | attachments, steps and labels |
| Configuration | Named profiles for local and CI, chosen by name | --report-profile=ci, in either config file |
||
| Prints what a run resolved, and which layer decided it | pytest-html-reporter config |
|||
| CI integration | A first-party GitHub Action | publishes it and comments on the PR | ||
| A first-party VS Code extension | the last run in your sidebar |
Checked in September 2026 against the source of pytest-html 4.2.0 and allure-pytest 2.16.0, and the Allure Report 2 and 3 documentation. A cell that needs a flag, a helper or a particular Allure version names it; an amber mark is a yes that arrives through a separate integration.
A pass/fail count tells you how much went wrong. Here is the whole of what the report tells you instead — forty-four of them, in one HTML file, with nothing to switch on.
--title names the report, --environment puts a badge beside it.--cov-fail-under rather than at a guess.coverage.json or a data file, recognised by content, not by name.jira("PROJ-123") becomes a badge that opens the ticket; owner and severity need no pattern at all.async with step(...), and gathered work that comes back as the siblings it was.pytest path::test built from the node id and quoted for a shell.trivial and two at blocker are not the same run.Rerun count opens the trail of what each attempt failed with.local and ci written down once, chosen in a word, overridable per job.The plugin writes the report. The Action publishes it from CI and comments on the pull request. The extension puts the same results in your editor sidebar.
The pytest plugin that generates the report.
Run the suite, keep the history, publish the page, comment on the PR.
The same results in the editor sidebar, one click from the code.
Installation, every command-line flag, the Python API, the report tour, the GitHub Action and the VS Code extension — all of it documented here.