Test Coverage with Jest's Built in Coverage
Introduction
Code coverage shows how much of your code is tested. Jest's built in coverage can easily be used to show code coverage reports.
Concepts
Coverage reports show:
- Statements covered.
- Branches (if/else) covered.
- Functions covered.
- Lines covered.
Helps identify untested parts of your code.
Examples
Running Coverage with Jest
Jest includes built-in coverage reporting, so no additional packages are required.
Run coverage directly from the command line:
npx jest --coverage
Or add a script to your package.json:
"scripts": {
"test": "jest",
"coverage": "jest --coverage"
},
Running coverage will output a report showing:
- Statements : 80% ( 8/10 )
- Branches : 75% ( 3/4 )
- Functions : 100% ( 2/2 )
- Lines : 80% ( 8/10 )
Challenges
1. Run jest coverage
- Run jest coverage on your project and check coverage.
💡 Show Answer
npm run coverage
2. Improve coverage by adding tests
- Add tests until coverage improves, then rerun.
💡 Show Answer
// add missing branch test to increase coverage
3. grade function test
- Write a function with an if/else branch.
- See how coverage reports branch testing.
💡 Show Answer
// testCode.js
function checkNumber(num) {
if (num > 0) {
return "Positive";
} else if (num < 0) {
return "Negative";
} else {
return "Zero";
}
}
module.exports = { checkNumber };
// testCode.test.js
const { checkNumber } = require("./testCode");
test("checkNumber returns 'Positive' for positive numbers", () => {
expect(checkNumber(5)).toBe("Positive");
});
test("checkNumber returns 'Negative' for negative numbers", () => {
expect(checkNumber(-3)).toBe("Negative");
});
test("checkNumber returns 'Zero' for zero", () => {
expect(checkNumber(0)).toBe("Zero");
});
4. Unused code uncovered
- Write a function with unused code and observe how coverage reports it.
💡 Show Answer
function unused(){
return "never tested";
}
// shows up as uncovered in report
5. HTML report
- Configure Jest to output an HTML report and open it in a browser.
💡 Show Answer
// Add a jest config file jest.config.js
module.exports = {
collectCoverage: false, // We'll enable this via command line
coverageDirectory: 'coverage',
coverageReporters: [
'text',
'html',
'lcov'
],
collectCoverageFrom: [
'**/*.js',
'!**/*.test.js',
'!**/*.spec.js',
'!**/node_modules/**',
'!**/coverage/**',
'!jest.config.js'
],
testEnvironment: 'node'
};
6. Coverage thresholds
- Add coverage thresholds in jest.config.js to fail below 80%.
💡 Show Answer
// Update jest.config.js
module.exports = {
collectCoverage: false, // We'll enable this via command line
coverageDirectory: "coverage",
coverageReporters: ["text", "html", "lcov"],
collectCoverageFrom: [
"**/*.js",
"!**/*.test.js",
"!**/*.spec.js",
"!**/node_modules/**",
"!**/coverage/**",
"!jest.config.js",
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
testEnvironment: "node",
};
7. Exclude files
- Exclude the test file from coverage with and see the effect.
💡 Show Answer
// Update jest.config.js to include
collectCoverageFrom: [
"**/*.js",
"!**/*.test.js",
"!**/*.spec.js",
"!**/node_modules/**",
"!**/coverage/**",
"!jest.config.js",
// Additional exclusion examples:
// "!**/dist/**", // Exclude build/dist directories
// "!**/build/**", // Exclude build directories
// "!**/*.config.js", // Exclude all config files
// "!**/scripts/**", // Exclude scripts directory
// "!**/docs/**", // Exclude documentation
// "!**/examples/**", // Exclude example files
// "!**/migrations/**", // Exclude database migrations
// "!**/seeds/**", // Exclude database seeds
// "!index.js", // Exclude specific files
],
8. Untested error handling
- Show how untested error handling paths reduce coverage.
💡 Show Answer
function safeDivide(a, b) {
if (b === 0) throw new Error();
return a / b;
}
module.exports = { safeDivide };
9. Incremental coverage
- Add a test at a time to see how coverage changes (any project)
💡 Show Answer
// add one test at a time and rerun coverage to see % increase
10. Async coverage
💡 Show Answer
// testCode.js
async function fetch(flag) {
if (flag) return "ok";
else throw "fail";
}
module.exports = { fetch };
// testCode.test.js
const { fetch } = require("./testCode");
test("ok", async () => {
expect(await fetch(true)).toBe("ok");
});
test("fail", async () => {
await expect(fetch(false)).rejects.toBe("fail");
});