Fix backend test coverage gaps. Use when CI output or test-backend --coverage reports missing lines, like "ERROR: path/to/file.py no longer has complete backend test coverage".
Fix backend test coverage gaps for Zulip's enforced 100% coverage files.
Use this skill when:
not_yet_fully_covered has reached 100% and should be
promoted to enforced, or coverage should be added to reach 100%IMPORTANT: Never run the full test suite with --coverage — it takes
a very long time. Always target the specific test module that covers
the file with missing coverage.
./tools/test-backend --skip-provision-check --coverage --no-cov-cleanup \
--no-html-report zerver.tests.test_specific_module
If invoked with an argument, use it: $ARGUMENTS
If the CI output names the source file but not the test module, use
git grep to find which test file imports or tests the relevant code.
./.claude/skills/fix-backend-coverage/analyze-coverage <source_file_with_missing_coverage>
With no arguments, checks all enforced files for missing coverage (useful after a targeted test run to see what's still missing).
The script loads var/.coverage and reports:
Read the source at each missing line and classify it:
| Line type | Fix |
|---|---|
| Dead code (unreachable branch) | Simplify/remove the dead branch |
| Error-only test path (assert, fail) | Add # nocoverage comment |
| Missing test coverage | Write a test that exercises the line |
| Newly 100% covered file | Remove from not_yet_fully_covered in tools/test-backend |
# nocoverage should only be used for lines that execute only when a
test fails or for truly unreachable defensive code. Never use it to
skip writing tests for reachable production code.
Re-run coverage on the same targeted test module and re-analyze:
./tools/test-backend --skip-provision-check --coverage --no-cov-cleanup \
--no-html-report zerver.tests.test_specific_module
./.claude/skills/fix-backend-coverage/analyze-coverage <source_file>
Confirm 0 missing lines, then lint:
./tools/lint --skip-provision-check --fix --only=ruff,ruff-format <changed_files>
tools/coveragercvar/.coveragecoveragerc: # nocoverage, if False:,
raise NotImplementedError, raise AssertionError, if TYPE_CHECKING:,
@abstractmethod, @skip, and ... (ellipsis)tools/test-backend defines two lists:
enforce_fully_covered: all .py files matching source_files globs
minus those in not_yet_fully_coverednot_yet_fully_covered: files exempt from the 100% requirementnot_yet_fully_covered reaches 100%, it should be
removed from the list to promote it to enforced status--coverage; locally
you check with targeted runs + analyze-coverage