Configuring code coverage in Jest, the right way
If there is something that should be never overlooked in any project, be it JavaScript or any other language, that's code coverage.
In this brief tutorial we see how to configure code coverage in Jest, the popular testing framework for JavaScript.
If you're new to Jest, please read Jest Tutorial for Beginners: Getting Started With JavaScript Testing before getting started.
Code coverage configuration matters, here's why
Code coverage makes possible to spot untested paths in our code. It is an important metric for determining the health of a project.
I've seen enough misconfigured JavaScript projects where Jest coverage were reported incorrectly.
Let's see why this matters. Consider the following project structure:
├── src
│ ├── subtract.js
│ └── sum.js
└── __tests__
└── sum.spec.js
We have two files, subtract.js
and sum.js
in the src
folder, plus a test in sum.spec.js
. sum.js
is quite a simple function:
module.exports = function sum(a, b) {
return a + b;
};
To test this function we have the following test in place:
const sum = require("../src/sum");
describe("Sum", () => {
test("sum two numbers", () => {
expect(sum(1, 1)).toEqual(2);
});
});
If we run this test with ./node_modules/.bin/jest
we can see it passing. So far so good.
Let's now run Jest with code coverage. To enable code coverage in Jest we can pass the --coverage
flag from the command line:
./node_modules/.bin/jest --coverage
We can also configure Jest to run through an NPM script:
"scripts": {
"test": "jest"
},
Then, we can pass the flag as follows:
npm test -- --coverage
With Yarn we could also omit the double dash and just run yarn test --coverage
.
Now, by running Jest in coverage mode we should be able to see the following output:
What's wrong here?
Jest is collecting coverage only on the function under tests, not from the entire project. This means that despite we are seeing 100% coverage here, potentially we are testing only a fraction of our code.
To fix this we can pass another flag to Jest, --collectCoverageFrom
, where we can specify the path from which Jest should collect coverage:
npm test -- --coverage --collectCoverageFrom="./src/**"
By doing so we say to Jest to look in the whole src
folder for JavaScript files. By running the above command we can see the following output:
Now Jest is identify correctly what needs to be tested.
Key takeaway: always pass --collectCoverageFrom
and --coverage
to Jest from the command line, or configure collectCoverage
and collectCoverageFrom
in your Jest config.
We will see an example minimal configuration at the end of this post.
Configuring a coverage threshold
Code coverage is nothing by itself. What we are interested in, most of the time, is also a good amount of code coverage in unit testing.
Personally, I'm not fixated in 100% code coverage, but in the projects I work on I always strive for at least a 90%-95% of coverage.
How to enforce such a threshold in a way that a pipeline in CI fails, or our local test fails if we do not meet the desired coverage requirements? In Jest we can configure coverageThreshold
.
For example, suppose we want our tests to always fail if we don't reach at least a 90% of lines coverage. We can configure coverageThreshold
as follows, in package.json
:
{
...
"jest": {
"collectCoverage": true,
"collectCoverageFrom": ["./src/**"],
"coverageThreshold": {
"global": {
"lines": 90
}
}
}
}
By running npm test
with this configuration in place we should see the following error:
Jest: "global" coverage threshold for lines (90%) not met: 50%
Again, I'm not suggesting 90% or 100% code coverage as the final goal of our life as developers, but having a minimum coverage threshold to rely on ensures that we are always testing as many lines as we can.
coverageThreshold
is highly configurable as described in the official Jest documentation.
Key takeaway: always configure coverageThreshold
in your Jest config to ensure a coverage baseline.
Conclusion
If you're using Jest, here are three options that should always be present in your Jest configuration:
collectCoverage
collectCoverageFrom
coverageThreshold
As a good starting point for any project, here's a minimal package.json
configuration on which you can build up (Jest can be also configured via jest.config.js
, or jest.config.ts
):
{
"jest": {
"collectCoverage": true,
"collectCoverageFrom": ["./src/**"],
"coverageThreshold": {
"global": {
"lines": 90
}
}
}
}
Make sure to adjust collectCoverageFrom
and coverageThreshold
to suit your own project/needs.
Thanks for reading!