Configuring code coverage in Jest, the right way

In this brief tutorial we see how to configure code coverage for Jest, the right way.

Jest code coverage

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:

Jest wrong code coverage configuration

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:

Jest correct code coverage configuration

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!

Valentino Gagliardi

Hi! I'm Valentino! I'm a freelance consultant with a wealth of experience in the IT industry. I spent the last years as a frontend consultant, providing advice and help, coaching and training on JavaScript, testing, and software development. Let's get in touch!