Coveralls is a hosted code-coverage service that records what percentage of your Django code your tests exercise, shows line-by-line coverage on every pull request, and renders a coverage badge for your README. To wire it up in 2026 you connect your GitHub (or Bitbucket) repository to coveralls.io, then let your CI run the test suite under coverage and push the report up — the canonical path today is GitHub Actions with the coverallsapp/github-action, or the coveralls Python package if you prefer.
This guide focuses on the Coveralls service and CI integration. For the basics of measuring coverage locally with coverage.py and pytest-cov, see our companion guide on checking test coverage in Django code with Coveralls.
Key takeaways
- Coveralls is the dashboard, not the measurer.
coverage.py/pytest-covproduce the numbers; Coveralls stores, visualizes, and trend-tracks them across CI runs and pull requests. - GitHub Actions is the modern path. The old Travis-only, Python 2 recipe is obsolete — run tests in an Actions workflow and upload the report from there.
- Two upload options: the
coverallsapp/github-action(least config) or thecoverallsPython package (coveralls --service=github). - Public/open-source repos are free; private repos need a paid plan — the same tiering Codecov uses.
- PR checks plus a README badge turn coverage into a visible, enforceable team signal.
How do I connect a Django repo to Coveralls?
- Sign in at coveralls.io with your GitHub or Bitbucket account.
- Click Add Repos and toggle on the repository you want to track.
- For public repositories that is all you need — Coveralls reads the build token from the GitHub Action automatically. For private repositories, copy the repo token Coveralls shows you and store it as an Actions secret (for example
COVERALLS_REPO_TOKEN). - Add a coverage step to your CI (next section) and push a commit. The first report appears on your Coveralls dashboard seconds after the build finishes.
What does a GitHub Actions workflow for Coveralls look like?
Create .github/workflows/tests.yml. The workflow installs dependencies, runs the Django test suite under coverage, emits an LCOV report, and hands it to the Coveralls action:
name: tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install coverage
- name: Run Django tests with coverage
run: |
coverage run --source='.' manage.py test
coverage lcov # writes ./coverage.lcov
- name: Upload coverage to Coveralls
uses: coverallsapp/github-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
file: coverage.lcovGitHub Actions injects GITHUB_TOKEN automatically, so public repositories need no extra secrets. The coverallsapp/github-action reads coverage.lcov by default; point file: at wherever your report actually lands.
How do I upload coverage with the coveralls Python package?
If you run your suite with pytest or use a CI other than Actions, the coveralls Python package is the alternative. Install it, run tests under coverage, then call coveralls --service=github with a GITHUB_TOKEN in the environment:
- name: Install dependencies
run: pip install -r requirements.txt pytest pytest-cov coveralls
- name: Run tests with pytest
run: pytest --cov=. --cov-report= # writes the .coverage data file
- name: Upload coverage to Coveralls
run: coveralls --service=github
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Which Coveralls upload method should I pick?
| Method | Extra install | Upload step | Best for |
|---|---|---|---|
coverallsapp/github-action |
None | uses: coverallsapp/github-action@v2 |
Most GitHub Actions projects; least config |
coveralls Python package |
pip install coveralls |
coveralls --service=github |
Non-Actions CI, or custom Python upload flows |
Both send the same data to coveralls.io — pick one per workflow, not both.
How do I configure what coverage measures?
Tell coverage which code counts and which to ignore, so migrations, settings, and test files do not dilute your numbers. A minimal .coveragerc in your project root:
[run]
source = .
omit =
*/migrations/*
*/tests/*
*/venv/*
manage.py
*/settings/*
*/wsgi.py
*/asgi.py
[report]
show_missing = TruePrefer pyproject.toml? Use the [tool.coverage.run] table instead:
[tool.coverage.run]
source = ["."]
omit = [
"*/migrations/*",
"*/tests/*",
"manage.py",
"*/wsgi.py",
"*/asgi.py",
]
[tool.coverage.report]
show_missing = trueKeep this list short — for a deeper walkthrough of source, omit, branch coverage, and the different --cov-report formats, see the companion coverage guide linked at the top.
How do I merge coverage from a parallel or matrix build?
When you test across several Python or Django versions with a build matrix, each job produces only a partial report. Coveralls stitches them together if you flag every matrix job with parallel: true and add a final job that sends parallel-finished: true:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -r requirements.txt coverage
- run: |
coverage run --source='.' manage.py test
coverage lcov
- name: Upload coverage (parallel)
uses: coverallsapp/github-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
file: coverage.lcov
flag-name: py-${{ matrix.python-version }}
parallel: true
finish:
needs: test
runs-on: ubuntu-latest
steps:
- name: Tell Coveralls the build is finished
uses: coverallsapp/github-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: trueHow do I add the coverage badge to my README?
Coveralls generates a badge per repository and branch. Copy the Markdown from your repo's Coveralls page, or build it yourself — replace your-org/your-repo and the branch name:
[](https://coveralls.io/github/your-org/your-repo?branch=main)Commit that to README.md and the badge refreshes after every build, giving contributors an at-a-glance coverage figure. Pair it with PR checks — set a minimum-coverage or no-drop rule in your Coveralls repo settings — so a regression blocks the merge instead of slipping through.
What should I do before turning Coveralls on?
Coveralls only reflects the tests you actually write — a green badge over a thin suite is false comfort. Before enabling it, make sure your Django views, forms, and models carry real assertions; our guide to Django unit test cases with forms and views is a solid starting point. If you are still on Travis CI, the upload step is nearly identical — see setting up Travis CI for a Django project — but new projects should default to GitHub Actions.
Coveralls vs Codecov: which should I choose?
Codecov is the most popular alternative to Coveralls, and for many teams it is the more feature-rich option — be honest about what you actually need. Both are SaaS coverage dashboards built around the same core workflow; the differences sit at the margins:
| Feature | Coveralls | Codecov |
|---|---|---|
| Hosting | SaaS (coveralls.io) | SaaS (codecov.io) |
| Public / open-source repos | Free | Free |
| Private repos | Paid plan | Paid plan |
| PR comments & checks | Yes | Yes (richer analytics) |
| Report formats | LCOV, Cobertura, and more | LCOV, Cobertura, XML, and more |
| GitHub Action | coverallsapp/github-action |
codecov/codecov-action |
| Matrix/parallel merge | parallel + parallel-finished |
flags, merged automatically |
| README badge | Yes | Yes |
If you want the simplest setup and only need a badge plus basic PR feedback, Coveralls is hard to beat. If you want richer PR analytics, component/flag breakdowns, and broader report-format support, evaluate Codecov.
Where does coverage fit in a real QA strategy?
Coverage is a useful signal, not a goal — 100% line coverage with weak assertions still ships bugs. Treat Coveralls as one layer alongside meaningful unit, integration, and end-to-end tests. If you would rather have a team own this end to end, MicroPyramid has done it across 50+ projects since 2014 as part of our software testing services.
Frequently Asked Questions
Is Coveralls free for Django projects?
Coveralls is free for public, open-source repositories; private repositories require a paid plan. That is the same tiering Codecov uses, so the decision between the two rarely comes down to access. You only need a GitHub or Bitbucket account to start tracking a public Django repo.
Do I still need Travis CI to use Coveralls?
No. Travis CI still works, but GitHub Actions is the canonical CI for new Django projects in 2026, and the Coveralls upload is a single action or command either way. Migrate the old Travis-and-Python-2 recipe to an Actions workflow like the one above; the coverage commands themselves are unchanged.
What is the difference between coverage.py and Coveralls?
coverage.py — and pytest-cov, which wraps it — measures coverage on your machine or CI runner and writes a report file. Coveralls is the hosted service that ingests that report, keeps history, draws the trend graph, comments on pull requests, and serves the README badge. You need both: one to measure, one to display.
How do I make a pull request fail when coverage drops?
Set a coverage rule in your Coveralls repository settings — for example, fail if total coverage decreases or falls below a chosen threshold. Coveralls then posts a commit status that GitHub treats as a required check, so a coverage regression blocks the merge until it is fixed.
Why does Coveralls show 0% or "no coverage report found"?
The usual causes are a report that was never generated, a wrong file path, or a source setting that does not match your package layout. On pull requests from forks the token can be missing — use coverallsapp/github-action, which handles fork tokens, or restrict uploads to same-repo branches.
Can I report coverage from a matrix of Python and Django versions?
Yes. Mark each matrix job with parallel set to true when it uploads, then add one final job that sends parallel-finished set to true. Coveralls waits for every parallel job, merges their partial reports, and publishes a single combined coverage number for the build.