Testing JavaScript
Code Coverage
Master code coverage in JavaScript testing. Understand statement, branch, function, and line coverage metrics and how to generate report files.
1. Introduction
Code Coverage is a metric that measures the percentage of your application's source code that is executed when running your automated test suite. It helps you identify untested files, functions, or execution branches.
2. Why It Matters
Writing tests doesn't guarantee that all code is verified. If your application has complex if-else branches, your tests might only cover the success path, leaving error-handling paths untested. Code coverage reports highlight these untested blocks, helping you improve test reliability.
3. Real-World Analogy
Think of a Warehouse Security Guard Patrol Log:
- Untracked Patrol: The guard claims they checked the building. Everything seemed fine. However, they only walked down the main hallways, skipping the storage rooms and fire exits.
- Code Coverage (GPS Checkpoint Tracker): The guard carries a scanner that registers checkpoints. When they complete their patrol, the tracker generates a report: "85% of rooms visited. Untested rooms: Back storage room 4 and Emergency Exit B." You get a clear, mathematical record of what areas were inspected and what areas were missed.
4. Types of Code Coverage
Coverage reports divide source code metrics into four categories:
• Statement Coverage: The percentage of statements in the code that were executed.
• Branch Coverage: The percentage of control structures (like if statements or switch cases) executed. This is critical for catching untested conditional branches.
• Function Coverage: The percentage of functions or methods declared in the code that were called.
• Line Coverage: The percentage of executable lines of code that were run.
5. Generating Coverage Reports
Jest includes a built-in coverage tool using Istanbul. You can generate a coverage report by running Jest with the --coverage flag:
This command prints a table summary to the console and generates a detailed HTML report inside a local /coverage folder. You can open /coverage/lcov-report/index.html in your browser to inspect which lines are highlighted in green (executed) or red (unexecuted).
6. Practical Example
Consider this validation helper:
If you write a single test that passes an admin user, your coverage report will show:
• Statement Coverage: 75%
• Branch Coverage: 50% (Branch B was never executed!)
To reach 100% branch coverage, you must add a second test case that passes a non-admin user.
7. Common Mistakes
- Targeting 100% code coverage as a strict requirement: Chasing 100% coverage can lead to writing tests that assert trivial properties (like getters and setters) or writing weak assertions that do not verify actual behavior just to increase coverage. Focus on writing high-quality tests for critical business logic instead of chasing metrics.
8. Quick Quiz
Q1: Which type of code coverage checks that both the true and false conditions of an 'if' statement have been executed by tests?
A) Function Coverage
B) Branch Coverage
Answer: B — Branch coverage measures the execution of all conditional pathways, ensuring both true and false paths are verified.
9. Scenario-Based Challenge
The Multi-Branch Shipping Discount Reporter:
An application calculates shipping discounts based on membership status, price, and region. Write a Jest test suite that uses the --coverage report tool to locate and cover all conditional branches, ensuring 100% branch coverage.
10. Debugging Exercise
Explain why this test suite reports 100% line coverage but fails to prevent a crash when an error is thrown:
// utils.js export function formatData(str) { return str.trim(); }// utils.test.js test('formats values', () => { formatData(' hello '); // Executes the line! Reports 100% coverage! });
// Bug: running formatData(null) throws a TypeError, but tests never checked null inputs! Why?
View Solution
Diagnosis: Code coverage only measures which lines of code were executed; it does not measure the quality or completeness of your assertions. The test executed the function with a valid string, reporting 100% coverage, but failed to test boundary conditions (like passing null or undefined) where errors could be thrown.
Fix: Add boundary test cases to verify error handling for invalid inputs:
test('handles null input safely', () => {
expect(() => formatData(null)).toThrow(); // Test boundary conditions!
});
11. Interview Questions
🟢 Q1: Explain why 100% code coverage does not guarantee bug-free code.
Answer: Code coverage measures which lines of code were executed during tests, but it does not measure the quality or completeness of your assertions.
• Missing Assertions: A test can execute a function without checking its return value, reporting 100% coverage while failing to verify behavior.
• Boundary Conditions: Coverage checks do not verify that your code handles unexpected inputs (like null, undefined, or negative numbers) correctly.
• Logic Bugs: Coverage cannot detect if your code implements the wrong business logic, even if every line is executed.
12. Production Considerations
- • Coverage Gates: In production environments, configure coverage thresholds inside your
jest.config.jsfile (e.g. settingbranches: 80) to fail builds in your CI/CD pipeline if code coverage drops below the limit.