|
| 1 | +# Code Coverage with pytest-cov |
| 2 | + |
| 3 | +This project uses [pytest-cov](https://pytest-cov.readthedocs.io/) to measure test coverage. Coverage helps identify which parts of your code are tested and which need more attention. |
| 4 | + |
| 5 | +## Quick Start |
| 6 | + |
| 7 | +### Using the Tux CLI (Recommended) |
| 8 | + |
| 9 | +The easiest way to run coverage is through the built-in Tux CLI: |
| 10 | + |
| 11 | +```bash |
| 12 | +# Run tests with coverage |
| 13 | +poetry run tux dev test |
| 14 | + |
| 15 | +# Run tests without coverage (faster) |
| 16 | +poetry run tux dev test-quick |
| 17 | + |
| 18 | +# Generate coverage reports |
| 19 | +poetry run tux dev coverage --format=html |
| 20 | +poetry run tux dev coverage --format=xml |
| 21 | +poetry run tux dev coverage --fail-under=90 |
| 22 | + |
| 23 | +# Clean coverage files |
| 24 | +poetry run tux dev coverage-clean |
| 25 | +``` |
| 26 | + |
| 27 | +### Direct pytest Commands |
| 28 | + |
| 29 | +You can also run pytest directly: |
| 30 | + |
| 31 | +```bash |
| 32 | +# Basic coverage report in terminal |
| 33 | +poetry run pytest --cov=tux |
| 34 | + |
| 35 | +# With missing lines highlighted |
| 36 | +poetry run pytest --cov=tux --cov-report=term-missing |
| 37 | + |
| 38 | +# Generate HTML report |
| 39 | +poetry run pytest --cov=tux --cov-report=html |
| 40 | +``` |
| 41 | + |
| 42 | +### Using the Coverage Commands |
| 43 | + |
| 44 | +Coverage functionality is integrated into the main CLI: |
| 45 | + |
| 46 | +```bash |
| 47 | +# Run tests with coverage report |
| 48 | +poetry run tux dev coverage |
| 49 | + |
| 50 | +# Generate HTML report |
| 51 | +poetry run tux dev coverage --format=html |
| 52 | + |
| 53 | +# Clean coverage files |
| 54 | +poetry run tux dev coverage-clean |
| 55 | + |
| 56 | +# See all available options |
| 57 | +poetry run tux dev coverage --help |
| 58 | +``` |
| 59 | + |
| 60 | +## Configuration |
| 61 | + |
| 62 | +Coverage is configured in `pyproject.toml`: |
| 63 | + |
| 64 | +```toml |
| 65 | +[tool.coverage.run] |
| 66 | +source = ["tux"] |
| 67 | +branch = true |
| 68 | +parallel = true |
| 69 | +omit = [ |
| 70 | + "*/tests/*", |
| 71 | + "*/test_*", |
| 72 | + "*/__pycache__/*", |
| 73 | + "*/migrations/*", |
| 74 | + "*/venv/*", |
| 75 | + "*/.venv/*", |
| 76 | +] |
| 77 | + |
| 78 | +[tool.coverage.report] |
| 79 | +precision = 2 |
| 80 | +show_missing = true |
| 81 | +skip_covered = false |
| 82 | +exclude_lines = [ |
| 83 | + "pragma: no cover", |
| 84 | + "def __repr__", |
| 85 | + "raise AssertionError", |
| 86 | + "raise NotImplementedError", |
| 87 | + "if __name__ == .__main__.:", |
| 88 | + "@abstract", |
| 89 | +] |
| 90 | + |
| 91 | +[tool.pytest.ini_options] |
| 92 | +addopts = [ |
| 93 | + "--cov=tux", |
| 94 | + "--cov-report=term-missing", |
| 95 | + "--cov-report=html", |
| 96 | + "--cov-branch", |
| 97 | + "--cov-fail-under=80", |
| 98 | + "-v", |
| 99 | +] |
| 100 | +``` |
| 101 | + |
| 102 | +## Coverage Reports |
| 103 | + |
| 104 | +### Terminal Report |
| 105 | + |
| 106 | +Shows coverage statistics directly in the terminal: |
| 107 | + |
| 108 | +```text |
| 109 | +Name Stmts Miss Branch BrPart Cover Missing |
| 110 | +--------------------------------------------------------------------- |
| 111 | +tux/utils/constants.py 28 0 0 0 100.00% |
| 112 | +tux/utils/functions.py 151 151 62 0 0.00% 1-560 |
| 113 | +--------------------------------------------------------------------- |
| 114 | +TOTAL 179 151 62 0 15.64% |
| 115 | +``` |
| 116 | + |
| 117 | +### HTML Report |
| 118 | + |
| 119 | +Generates a detailed interactive HTML report in `htmlcov/`: |
| 120 | + |
| 121 | +```bash |
| 122 | +poetry run tux dev coverage --format=html |
| 123 | +# Generates htmlcov/index.html |
| 124 | + |
| 125 | +# Open the report in browser |
| 126 | +poetry run tux dev coverage --format=html --open |
| 127 | +# or open it separately |
| 128 | +poetry run tux dev coverage-open |
| 129 | +``` |
| 130 | + |
| 131 | +The HTML report provides: |
| 132 | + |
| 133 | +- **File-by-file coverage**: Click on any file to see line-by-line coverage |
| 134 | +- **Missing lines**: Highlighted lines that aren't covered by tests |
| 135 | +- **Branch coverage**: Shows which conditional branches are tested |
| 136 | +- **Search functionality**: Find specific files or functions |
| 137 | + |
| 138 | +### XML Report |
| 139 | + |
| 140 | +For CI/CD integration: |
| 141 | + |
| 142 | +```bash |
| 143 | +poetry run tux dev coverage --format=xml |
| 144 | +# Generates coverage.xml |
| 145 | +``` |
| 146 | + |
| 147 | +### JSON Report |
| 148 | + |
| 149 | +Machine-readable format: |
| 150 | + |
| 151 | +```bash |
| 152 | +poetry run tux dev coverage --format=json |
| 153 | +# Generates coverage.json |
| 154 | +``` |
| 155 | + |
| 156 | +## Coverage Targets |
| 157 | + |
| 158 | +- **Current target**: 80% overall coverage |
| 159 | +- **Goal**: Gradually increase coverage for new code |
| 160 | +- **Focus areas**: Utility functions, core business logic, and critical paths |
| 161 | + |
| 162 | +## Best Practices |
| 163 | + |
| 164 | +### 1. Write Tests for New Code |
| 165 | + |
| 166 | +Always write tests for new functionality: |
| 167 | + |
| 168 | +```python |
| 169 | +# tests/test_new_feature.py |
| 170 | +def test_new_feature(): |
| 171 | + result = new_feature("input") |
| 172 | + assert result == "expected_output" |
| 173 | +``` |
| 174 | + |
| 175 | +### 2. Use Coverage to Find Gaps |
| 176 | + |
| 177 | +Run coverage reports to identify untested code: |
| 178 | + |
| 179 | +```bash |
| 180 | +poetry run tux dev coverage | grep "0.00%" |
| 181 | +``` |
| 182 | + |
| 183 | +### 3. Exclude Appropriate Code |
| 184 | + |
| 185 | +Use `# pragma: no cover` for code that shouldn't be tested: |
| 186 | + |
| 187 | +```python |
| 188 | +def debug_function(): # pragma: no cover |
| 189 | + """Only used for debugging, don't test.""" |
| 190 | + print("Debug info") |
| 191 | +``` |
| 192 | + |
| 193 | +### 4. Focus on Critical Paths |
| 194 | + |
| 195 | +Prioritize testing: |
| 196 | + |
| 197 | +- **Core business logic** |
| 198 | +- **Error handling** |
| 199 | +- **Edge cases** |
| 200 | +- **Integration points** |
| 201 | + |
| 202 | +### 5. Branch Coverage |
| 203 | + |
| 204 | +Enable branch coverage to test all code paths: |
| 205 | + |
| 206 | +```python |
| 207 | +def process_data(data): |
| 208 | + if data: # Both True and False paths should be tested |
| 209 | + return process_valid_data(data) |
| 210 | + else: |
| 211 | + return handle_empty_data() |
| 212 | +``` |
| 213 | + |
| 214 | +## CI/CD Integration |
| 215 | + |
| 216 | +### GitHub Actions |
| 217 | + |
| 218 | +```yaml |
| 219 | +- name: Run tests with coverage |
| 220 | + run: | |
| 221 | + poetry run tux dev coverage --format=xml |
| 222 | +
|
| 223 | +- name: Upload coverage to Codecov |
| 224 | + uses: codecov/codecov-action@v3 |
| 225 | + with: |
| 226 | + file: ./coverage.xml |
| 227 | +``` |
| 228 | +
|
| 229 | +## Common Commands |
| 230 | +
|
| 231 | +### Tux CLI Commands |
| 232 | +
|
| 233 | +```bash |
| 234 | +# Basic testing |
| 235 | +poetry run tux dev test # Run tests with coverage |
| 236 | +poetry run tux dev test-quick # Run tests without coverage |
| 237 | + |
| 238 | +# Coverage reports |
| 239 | +poetry run tux dev coverage # Terminal report (default) |
| 240 | +poetry run tux dev coverage --format=html # HTML report |
| 241 | +poetry run tux dev coverage --format=html --open # HTML report + open browser |
| 242 | +poetry run tux dev coverage --format=xml # XML report for CI |
| 243 | +poetry run tux dev coverage --format=json # JSON report |
| 244 | +poetry run tux dev coverage --fail-under=90 # Set coverage threshold |
| 245 | + |
| 246 | +# Advanced options |
| 247 | +poetry run tux dev coverage --quick # Quick coverage check (no detailed reports) |
| 248 | +poetry run tux dev coverage --specific=tux/utils # Test specific module |
| 249 | +poetry run tux dev coverage --clean # Clean coverage files before running |
| 250 | +poetry run tux dev coverage-clean # Clean coverage files only |
| 251 | +poetry run tux dev coverage-open # Open HTML report in browser |
| 252 | +``` |
| 253 | + |
| 254 | +## Troubleshooting |
| 255 | + |
| 256 | +### No Coverage Data |
| 257 | + |
| 258 | +If you see "No data was collected": |
| 259 | + |
| 260 | +1. Ensure tests import the code being tested |
| 261 | +2. Check that the source path is correct in `pyproject.toml` |
| 262 | +3. Verify tests are actually running |
| 263 | + |
| 264 | +### Low Coverage Warnings |
| 265 | + |
| 266 | +If coverage is below the threshold: |
| 267 | + |
| 268 | +1. Add tests for uncovered code |
| 269 | +2. Review if the threshold is appropriate |
| 270 | +3. Use `--cov-report=term-missing` to see missing lines |
| 271 | + |
| 272 | +### Performance Issues |
| 273 | + |
| 274 | +For faster test runs during development: |
| 275 | + |
| 276 | +```bash |
| 277 | +# Skip coverage for quick tests |
| 278 | +poetry run pytest tests/test_specific.py |
| 279 | + |
| 280 | +# Use the quick option |
| 281 | +poetry run tux dev coverage --quick |
| 282 | +``` |
| 283 | + |
| 284 | +## Resources |
| 285 | + |
| 286 | +- [pytest-cov Documentation](https://pytest-cov.readthedocs.io/) |
| 287 | +- [Coverage.py Documentation](https://coverage.readthedocs.io/) |
| 288 | +- [Testing Best Practices](https://docs.pytest.org/en/latest/explanation/goodpractices.html) |
0 commit comments