Test Coverage Guide¶
This guide explains how to measure and improve test coverage in aiohomematic.
Running Coverage¶
Basic Coverage¶
Run tests with coverage:
HTML Coverage Report¶
Generate an interactive HTML report:
View the report:
open htmlcov/index.html # macOS
xdg-open htmlcov/index.html # Linux
start htmlcov/index.html # Windows
Terminal Coverage Report¶
Show missing lines in terminal:
XML Coverage Report¶
Generate XML report for CI/codecov:
JSON Coverage Report¶
Generate JSON report:
Coverage Configuration¶
Current Targets¶
- Minimum Coverage: 85%
- Branch Coverage: Enabled
- Missing Lines: Shown in reports
Excluded Files¶
The following files are excluded from coverage:
aiohomematic/validator.py- CLI validatoraiohomematic/exceptions.py- Exception definitionsaiohomematic/central/rpc_server.py- XML-RPC server (hard to test)aiohomematic/__main__.py- Entry pointaiohomematic/hmcli.py- CLI tool
Excluded Lines¶
Certain patterns are excluded from coverage:
pragma: no cover- Manual exclusiondef __repr__- Debug representationsraise AssertionError- Defensive coderaise NotImplementedError- Abstract methodsif TYPE_CHECKING:- Type checking blocks@overload- Type overloads@abstractmethod- Abstract methods...- Protocol method stubsif __name__ == "__main__":- Entry points
Component Coverage¶
Codecov tracks coverage by component:
Central Module¶
Client Module¶
Model Module¶
Store Module¶
Improving Coverage¶
Finding Uncovered Code¶
- Generate HTML report to see highlighted code:
-
Open the report and look for red-highlighted lines
-
Sort by coverage to find modules with low coverage
Writing Coverage Tests¶
Focus on:
- Edge cases - Test boundary conditions
- Error paths - Test exception handling
- Branch coverage - Test all conditional branches
- Integration paths - Test component interactions
Example:
def test_device_not_found():
"""Test handling of missing device."""
device = central.get_device(address="NONEXISTENT")
assert device is None
def test_device_found():
"""Test successful device lookup."""
device = central.get_device(address="VCU0000001")
assert device is not None
assert device.address == "VCU0000001"
Coverage Tips¶
- Start with critical paths - Cover main functionality first
- Use fixtures - Reuse test setup
- Test one thing - Keep tests focused
- Mock external dependencies - Isolate units under test
- Check branch coverage - Ensure all conditions are tested
Continuous Integration¶
GitHub Actions¶
Coverage is automatically checked on:
- Pull requests
- Pushes to
masteranddevel
Codecov comments on PRs with:
- Coverage change
- Component coverage
- Uncovered lines
Coverage Requirements¶
- Project coverage must not decrease by more than 9%
- Patch coverage for new code must be at least 95%
- Changes must not reduce coverage
Codecov Dashboard¶
View detailed coverage at:
Features:
- Sunburst chart - Visual coverage by component
- File browser - Line-by-line coverage
- Comparison - Compare branches and PRs
- Trends - Coverage over time
Coverage Configuration Files¶
pyproject.toml¶
Coverage settings in [tool.coverage]:
[tool.coverage.run]
branch = true
source = ["aiohomematic"]
omit = [...]
[tool.coverage.report]
show_missing = true
fail_under = 85.0
sort = "cover"
codecov.yml¶
Codecov configuration:
Troubleshooting¶
No Coverage Data¶
If no coverage is collected:
- Ensure pytest-cov is installed:
- Check source path is correct:
Coverage Too Low¶
If coverage fails CI:
- Check the diff - See what code isn't covered
- Add tests for uncovered lines
- Use
pragma: no coversparingly for truly untestable code
Parallel Coverage¶
For parallel test execution:
Coverage data is automatically combined.
Best Practices¶
- Run coverage locally before pushing
- Review HTML report to understand gaps
- Focus on business logic - High-value code first
- Don't chase 100% - 85% with good tests is better than 100% with poor tests
- Test behavior, not implementation - Coverage is a metric, not the goal
See Also¶
- pytest-cov documentation
- Codecov documentation
- CLAUDE.md - Testing guidelines