You have written unit, integration and E2E tests by now. But how do you know you have not left a dark alley in the code that no test ever looked into?
The legion's cartographer has a method for this. He takes a map of the Empire and marks every road a scout has walked. Whatever stays blank, nobody checked. Test coverage is exactly such a map: you run the tests, and the tool records which lines of code actually executed.
You order the report with a single command:
1npm run test:covThat script is nothing more than
jest --coverage - Jest runs all the tests while watching which parts of the code it touches along the way. The result looks like this:1Statements : 87.5% ( 175/200 )
2Branches : 83.3% ( 50/60 )
3Functions : 90.0% ( 45/50 )
4Lines : 88.0% ( 176/200 )Four numbers, four different questions about the same code. They are worth separating, because they differ in sharpness.
Statements and Lines count executed instructions and lines - the coarsest measure, saying only "we passed this way". Functions asks whether every function was called at least once; useful for catching methods everyone forgot about.
The third number matters most. Branches measures conditional paths - whether every
if was exercised both when the condition held and when it did not. Look at this function:1function calculatePay(legionary: Legionary): number {
2 if (legionary.rank === 'CENTURION') {
3 return legionary.basePay * 2;
4 }
5 return legionary.basePay;
6}A single test with a centurion executes every line except
return legionary.basePay - statement coverage jumps high. But the "not a centurion" branch stays unchecked, and that is precisely where bugs like to sit. This is why branch coverage is always lower than statement coverage, and why it is the number that tells the truth about your tests.The report by itself enforces nothing. To stop coverage sliding week by week, we set a threshold in the Jest configuration:
1coverageThreshold: {
2 global: {
3 statements: 80,
4 branches: 75,
5 functions: 80,
6 lines: 80,
7 },
8},From now on a drop below the threshold ends in failure - the command returns a non-zero exit code, so it will also stop a CI build. Note that the threshold for
branches is lower than the rest. That is not an oversight: branches are inherently harder to reach, so a realistic threshold sits lower, keeping the requirement achievable rather than turning it into a ritual everyone works around.Here we reach the thing most easily forgotten while admiring pretty percentages. Coverage measures whether code ran - not whether it behaved correctly.
This test yields full coverage and checks nothing:
1it('calculates pay', () => {
2 calculatePay({ rank: 'CENTURION', basePay: 100 });
3});The function was called, so the cartographer marks the road as surveyed. But there is not a single
expect here - if the function returned a negative number or threw, the test would still pass green. Coverage says where the scout has been; it does not say whether he looked.Hence the practical rule, @name: treat coverage as a detector of blank spots, not as a grade. Low coverage is a hard signal that something went unchecked - worth investigating. High coverage proves nothing by itself. Chasing a round hundred percent usually ends in tests written for the counter: no assertions, but plenty of them on getters and configuration files.
So read the list of uncovered lines from the report rather than the percentage. That list points at the dark alleys, and among them check error handling and edge cases first - those hold the least frequently executed and most expensive branches.
The map of the Empire is drawn and the blank spots are visible:
npm run test:cov, that is jest --coverage,if in both variants - and it speaks best about test quality,coverageThreshold in the Jest configuration turns the threshold into a hard requirement: a drop below it breaks the build,expect gives full coverage and checks nothing,In the next lesson we will turn to performance testing - checking not whether the fort works, but how much pressure it withstands. For now remember: coverage is a map with the scouts' roads marked - blank spots tell you where nobody went, but a marked road does not prove the scout was looking for anything along it.