Test Coverage

How can you know if the tests you have written are enough ?

December 10th, 20224 mins read

test-coverage

OTHER ARTICLES IN SERIES


TABLE OF CONTENT


  1. What we need
  2. Setting the coverage option
  3. Viewing the coverage data
  4. Analyzing the coverage data
  5. Deciding where to collect coverage from
  6. Improving the firmness of our application using "coverage threshold"

What we need

The purpose of writing tests is to ensure that our application is functioning exactly how we want it to, without any errors.

However, after writing tests, how can we know if the written tests are enough to cover all important places in our application ?

Say hello to Jest "collect coverage".

With the collectCoverage option provided by jest, we can easily know how much of our code has been tested and how much of our code is left to test.

Setting the coverage option

Open you jest.config file and add the collectCoverage. Your file should now look like this:

jest.config.ts
import nextJest from "next/jest";

const createJestConfig = nextJest({
	dir: "./",
});

const customJestConfig = {
	setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
	testEnvironment: "jest-environment-jsdom",
	verbose: true,
	collectCoverage: true,
};

export default createJestConfig(customJestConfig);

With the collectCoverage option set, make sure you restart your terminal.

After running the test command again, you should see a table like this generated in your console:

Results after running tests

Sweet right šŸ˜…?

Viewing the coverage data

That's not all; If you look at the root folder of your application, you will see that a supposed "hidden" folder named coverage has been created for you.

Go into the folder, then into icov-report folder, open the index.html file in the browser.

Code editor files view

When you open the index.html file in the browser you would see a more beautiful graphical display like the one shown below.

It is simply a more detailed equivalent of the previous one displayed in our console above.

Coverage report viewed in browser (Folders)

Click on pages since that is our first point of concern.

Coverage report viewed in browser (Files)

You can see both pages of our application being listed (i.e about.tsx and index.tsx).

Analyzing the coverage data

If you look at the table in the previous image, you will notice that the table has five columns:

  1. File - The file which has the information we want to display
  2. Statements - Measures if all possible executable statements in your code are covered in your tests
  3. Branches - Measures if all possible branches (e.g if else statement) in your code are covered in your tests
  4. Functions - Measures if all the functions present in your code are covered in your tests
  5. Lines - More or less just like "statements"

Since our index.tsx is our main home feed page let's go into it. But before going into it let's take note of our scores i.e

  • Statement = 91.04%
  • Branches = 90.9%
  • Functions = 100%
  • Lines = 90.76%

Straight up, we can see the reason why none of the scores for our index file is 100% in any of the four criteria (i.e statements, branches, functions and lines).

The "red" highlighting shows us places of our code that were never tested. With this information, we know that we need to write tests for when our network request fails, (i.e line 39 in the image above) and for when a user tries to create a post and there is no text in the inputValue.

See the easy way collectCoverage made us know what was left out.

Deciding where to collect coverage from

By default, jest tries to collect coverage from all "functioning" files (i.e any file that is imported when our application runs) in our application. However, you can manually specify which files jest should check and which it shouldn't by using the collectCoverageFrom key in our "jest.config".

The collectCoverageFrom key takes an array of strings. Add "!" at the front of folders and files you don't want to cover.

jest.config.ts
import nextJest from "next/jest";

const createJestConfig = nextJest({
	dir: "./",
});

const customJestConfig = {
	setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
	testEnvironment: "jest-environment-jsdom",
	verbose: true,
	collectCoverage: true,
	collectCoverageFrom: [
		//Cover these files
		"pages/**/*.{js,jsx,ts,tsx}",
		//Dont cover these files
		"!**/node_modules/**",
		"!pages/_app.js",
		"!pages/api/**/*.{js,jsx,ts,tsx}",
	],
};

export default createJestConfig(customJestConfig);

Improving the firmness of our application using coverage threshold

If we are being realistic, most times, people do not have the time to write code for every test case, such that you have a 100% in the four parameters (i.e statement, branches, functions and lines) that coverage covers.

Jest gives us a nice coverageThreshold key, which we can use to specify a convenient percentage of our application that our tests must cover.

jest.config.ts
import nextJest from "next/jest";

const createJestConfig = nextJest({
	dir: "./",
});

const customJestConfig = {
	setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
	testEnvironment: "jest-environment-jsdom",
	verbose: true,
	collectCoverage: true,
		collectCoverageFrom: [
		//Cover these files
		"pages/**/*.{js,jsx,ts,tsx}",
		//Dont cover these files
		"!**/node_modules/**",
		"!pages/_app.js",
		"!pages/api/**/*.{js,jsx,ts,tsx}",
	],
	coverageThreshold: {
		global: {
			lines: 80,
			functions: 80,
			branches: 80,
			statements: 80,
		},
	},
};

export default createJestConfig(customJestConfig);

We have specified the global key, and set 80 percent as the threshold that we want for our various parameters. Now in our application, anytime our tests run and this threshold is not reached, we will see some sort of notification message in the console.

However, the purpose of setting threshold is not mainly for seeing a message in our console. We can tie this up to our production build, such that if our threshold is not met, the build fails.

With this we can be sure that at least the available tests in our application are either equal to or above our acceptable threshold.

Conclusion

We have successfully learnt how to:

  • enable the collectCoverage option in jest
  • analyse the output we get
  • use collectCoverageFrom to determine parts of our application to cover
  • and finally, how to use coverageThreshold to specify how much of our applcication must be tested.

I hope you use this knowledge to write robust tests that cover the entirety of your application as you want.

Thank you

Discuss on Twitter
David smiling

David Obodo is a Fullstack developer, with main focus on Frontend development.

David's articles are 'inspired' by times he found a simpler way to understand a concept or implement a feature and when he had a thought to share (well who doesn't every once in a while)?

David writes because teaching is the best way to solidify his own knowledge and to also ease the stress of the reader, with simple explanations.