Coverage.py
Coverage.py measures code coverage in Python programs, showing which lines of code are executed and which are untested.
Installation
pip install coverage
pip install pytest-cov # For pytest integration
Basic Commands
# Run coverage on Python script
coverage run myscript.py
# Run coverage with pytest
coverage run -m pytest tests/
# Generate coverage report to terminal
coverage report
# Generate HTML report
coverage html
# Generate XML report (for CI/CD)
coverage xml
# Show lines not covered
coverage report --skip-covered
Configuration
.coveragerc File
[run]
source = src/
omit = */tests/*
[report]
exclude_lines =
pragma: no cover
def __repr__
raise AssertionError
raise NotImplementedError
if __name__ == .__main__.:
if TYPE_CHECKING:
@abstractmethod
[html]
directory = htmlcov
pytest Integration
# Run pytest with coverage
pytest --cov=src/ tests/
# Generate HTML report with pytest
pytest --cov=src/ --cov-report=html tests/
# Show missing lines
pytest --cov=src/ --cov-report=term-missing tests/
# Check coverage threshold
pytest --cov=src/ --cov-fail-under=80 tests/
Measuring Coverage
Python Script
import coverage
cov = coverage.Coverage()
cov.start()
# Your code here
import mymodule
mymodule.my_function()
cov.stop()
cov.save()
cov.html_report(directory='htmlcov')
Command Line
# Measure coverage
coverage run -m pytest tests/
# Get report
coverage report
# Output:
# Name Stmts Miss Cover
# -----------------------------------------------
# src/calculator.py 20 2 90%
# src/utils.py 15 5 67%
# -----------------------------------------------
# TOTAL 35 7 80%
Coverage Reports
# Terminal report
coverage report
# Terminal report with missing lines
coverage report --show-missing
# JSON report
coverage json
# XML report (Cobertura)
coverage xml
# HTML report (interactive)
coverage html
# Open htmlcov/index.html
# Combine coverage from multiple runs
coverage combine
Exclude Lines from Coverage
# Exclude single line
def debug_only(): # pragma: no cover
print("Debug info")
# Exclude block
if __name__ == "__main__": # pragma: no cover
run_application()
# Using conditionals
import sys
if sys.version_info < (3, 8): # pragma: no cover
from backports import something
CI/CD Integration
GitHub Actions
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- run: pip install -r requirements.txt
- run: pytest --cov=src --cov-report=xml tests/
- uses: codecov/codecov-action@v2
with:
files: ./coverage.xml
GitLab CI
test:
script:
- pip install -r requirements.txt
- pytest --cov=src --cov-report=term --cov-report=xml tests/
coverage: '/TOTAL.*\s+(\d+%)$/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
Real World Example
# Install dependencies
pip install coverage pytest
# Create test file tests/test_math.py
# Create module src/math_ops.py
# Run coverage
coverage run -m pytest tests/
# Generate reports
coverage report
coverage html
coverage xml
# View HTML report
open htmlcov/index.html
Best Practices
- Aim for 80%+ code coverage
- Don’t chase 100% coverage on non-critical code
- Exclude infrastructure and debug code
- Use coverage reports to find untested edge cases
- Integrate coverage into CI/CD pipelines
- Track coverage trends over time
- Use branch coverage for better metrics
- Test error conditions and edge cases
Common Issues
# Coverage not detecting imports
coverage run --source=. -m pytest
# Parallel test execution
coverage run -p -m pytest tests/
coverage combine
coverage report
# Measuring coverage of specific module
coverage run --source=mymodule -m pytest tests/
Resources
Last updated: 2025-07-06|Edit on GitHub