pytest-html: turning a pytest run into a standalone HTML report
Plugin for generating HTML reports for pytest results
At a glance
- What is it?
- pytest-html is a pytest plugin that renders collected test results as an HTML page, shipped with a bundled JavaScript front end built from Sass and browserify. It fits teams that need a browsable report from CI, and it costs a Node toolchain and a second dependency on pytest-metadata.
- Who is it for?
- Adopt pytest-html when your CI consumers are people who need a readable pass and fail summary without parsing output, and skip it when a machine-readable result stream matters more or when adding Node to the build is not worth the change.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 10 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A pytest run produces a page instead of a scrollback buffer
pytest-html is a plugin for pytest that generates an HTML report for test results. The problem it addresses is the readability of CI output. A suite with a thousand tests prints a wall of dots, and a single failure buried in that wall is tedious to locate and impossible to share with someone who does not have the job log. The plugin turns the same run into a document with one entry per test, grouped for browsing, and the document can be archived as an artifact or opened from a link in a build page.
The audience is a team that already uses pytest and wants a report human reviewers can open, not a team looking for a test framework. The project is a plugin rather than a wrapper: it hooks into pytest's own run, so the report reflects the tests pytest collected and executed, and it does not define a separate test discovery or assertion model.
The project is published through the standard Python channels, with badges pointing to PyPI and to conda-forge, and it requires Python 3.9 or newer. Its distribution metadata also lists support for CPython and PyPy across Python 3.9 through 3.14, which tells you the supported interpreter range is a maintained decision rather than an accident of one environment.
The report is a bundled JavaScript app, not a plain template dump
The interesting architectural detail is that pytest-html ships a front end. The package manifest defines a build that compiles a Sass stylesheet into src/pytest_html/resources/style.css and bundles a JavaScript entry point into src/pytest_html/resources/app.js. The layout is also a JavaScript project with its own linting and unit test commands, and the Python side depends on jinja2 and pytest-metadata, so the plugin sits on both stacks at once.
That combination explains a lot about how the project evolves. A change to the report's behaviour means editing Python and rebuilding assets, and the repository carries a .prettierrc and an eslint config for the front end code alongside tox.ini and a .coveragerc for the Python side. The rendered page is therefore a static, self-contained document: the report works from a file path or a CDN-hosted artifact link, without a server or a database behind it.
Test result metadata comes from pytest-metadata, which pytest-html requires at version 2 or above. That dependency is worth remembering when you evaluate the plugin, because it means the metadata layer is shared with other reporting tools rather than locked inside this one.
There is no documented install command, and where the docs live
One gap in the README is worth stating plainly: it does not give a pip install line, a conda install line, or a command line flag for producing a report. It links to the hosted documentation at pytest-html.readthedocs.io for usage and to a separate changelog page for release notes, and it points to the issue tracker and the code.
So the practical first step is to read the usage page rather than to copy a line from the README. The distribution itself is available on PyPI and on conda-forge, both of which the README links to with badges, so the package can be installed into an existing environment either way.
The other part of a first real use happens in the source tree, and the build commands are fully specified. The manifest defines a build that compiles the stylesheet and bundles the front end, and an all target that lints, runs the front end unit tests and then performs both builds:
"build": "npm run build:css && npm run build:jsapp",
"build:css": "sass --no-source-map --no-error-css src/layout/css/style.scss src/pytest_html/resources/style.css",
"build:jsapp": "browserify ./src/pytest_html/scripts/index.js > ./src/pytest_html/resources/app.js",
"all": "npm run lint && npm run unit && npm run build:css && npm run build:jsapp"The lint and unit steps rely on eslint, mocha, nyc, chai, sinon and mock-local-storage, all declared as dev dependencies, and the unit run needs a browser-like environment because of the mock-local-storage flag.
The Node toolchain is the upgrade cost nobody mentions in the badge
Adding pytest-html to a Python project is not a pure Python operation. Any contribution that touches the report's appearance or behaviour goes through sass and browserify, and the front end has its own test suite run under mocha with coverage collected by nyc. In practice that means a CI job that has both a Python toolchain and a Node one, and a contributor who has to understand two build systems before touching the report.
This is a real trade-off, not a defect. A static report with sorting, filtering and collapsible sections is far more pleasant to read than a raw log, and building it in the browser is the straightforward way to get there. But teams that only need a machine-readable result stream, or that would rather keep their build image small, are paying for a front end they may never look at.
There is also a subtlety in the packaging. The version is declared dynamic in pyproject.toml, with hatch-vcs and hatchling as the build backend requirements, so the release version is derived from the repository history at build time rather than written into a file by hand. That is a sound practice for a plugin distributed from source snapshots, but it means a version number is not visible in the tracked project metadata and has to be read from the built artifact.
Two things the plugin does not tell you about a failing run
The first limitation is about comparison, and the documentation is quiet on it. The README does not document any facility for comparing two runs, for tracking a failure across commits, or for trending pass rates over time. It presents a report of one run. If your team wants to answer a question like whether a test that fails on a release branch also failed last week, that answer is not in this tool, and the workflow is to keep the generated files yourself.
The second limitation concerns licensing metadata, and here the sources disagree in a way an engineer should check. The repository listing records the LICENSE as a custom licence that GitHub cannot classify. The project metadata in pyproject.toml declares MPL-2.0 in its license field, and the classifiers list the Mozilla Public License 2.0. The README carries a licence badge pointing at the LICENSE file. Those readings are consistent with a recognised open source licence, but the classification mismatch means the authoritative answer is the text of the LICENSE file in the tree you actually vendor.
There is also no stated support for JUnit-style XML output, no mention of retry behaviour and no built-in history store, so a pipeline that already consumes XML reports from another tool has to keep that tool alongside this one.
Building on pytest-metadata instead of adding a second reporter
The alternative worth comparing is not another HTML reporter, it is writing your own consumer of pytest-metadata. That library is already a dependency of pytest-html at version 2 or above, and it exists to collect metadata from a pytest run and expose it in a structured form, which a plugin can then render however it likes. A team that wants a small custom report, a Slack summary or a database row can take that route and skip the front end entirely.
The difference in approach is the whole trade-off in one line. pytest-html takes a working browser front end, a Sass and browserify build, and packaged assets as the price of a polished, self-contained page. The metadata route takes your own rendering code as the price, and gives back control over the output format and no extra runtime dependency beyond pytest-metadata.
The other difference is in the audience. A self-contained HTML artifact can be attached to a build and opened by anyone, including people who never touch the test suite. A custom consumer is faster to change but ties the report to whatever system you already maintain. If the report is consumed by a browser rather than by a script, pytest-html is the shorter path.
A 2026 release after a long gap, with commits continuing since
The release history explains why you may have an old opinion of this plugin. Version 4.1.0 and 4.1.1 both landed in November 2023, and the next listed release, 4.2.0, is dated 2026-01-19. That is a gap of more than two years between published versions, which is long enough for a plugin in the pytest ecosystem to need attention after a pytest major release.
The commit record tells a different story from the release list. The last push was on 2026-10-13, and the default branch is master, so work has continued on the branch through 2026. When judging maintenance, the two facts need to be read together: the branch is being worked on, but a released 4.2.0 is what most installs are still using, and the changelog in the hosted documentation is where the differences between them are recorded.
For an adopter, the practical consequence is to pin the version deliberately. pytest-html declares support for pytest 7 and above, and the version you install determines which pytest releases it has been exercised against.
Editorial conclusion
Adopt pytest-html when your CI consumers are people who need a readable pass and fail summary without parsing output, and skip it when a machine-readable result stream matters more or when adding Node to the build is not worth the change. Before installing, check the LICENSE file yourself, because the repository listing classifies it as a custom licence while pyproject.toml declares MPL-2.0, and confirm that release 4.2.0 from 2026-01-19 covers the pytest version your suite runs on, given that the last push was on 2026-10-13.
Frequently asked questions
What does pytest-html add to a normal pytest run?
It is a pytest plugin that generates an HTML report for test results, with a self-contained front end bundled from Sass and browserify sources. It does not change test discovery or assertions, it renders the run that pytest already performed.
How do I install pytest-html and produce a report?
The README does not spell out an install command or a report flag. It links to the hosted usage documentation at pytest-html.readthedocs.io and shows that the package is published on PyPI and on conda-forge, so those are the sources to follow for the exact command.
What licence is pytest-html released under?
The project metadata in pyproject.toml declares MPL-2.0 and the classifiers list the Mozilla Public License 2.0. The repository listing shows the LICENSE as a custom licence GitHub cannot classify, so read the LICENSE file in the tree you vendor.
Does pytest-html compare test results between runs?
The README does not document any comparison, history or trend feature, so the report covers a single pytest run. Keeping and diffing the generated files yourself is the way to track a failure across commits.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pytest-dev-pytest-html)Was this useful?