Component Code Coverage in Cypress v10

How to instrument React component tests and produce the code coverage report.

Let's say you are enjoying the Cypress component testing, but wondering how to get the code coverage information from the component tests? Back in my days of cypress-react-unit-test the code coverage was built-in... But now it requires adding it in. Luckily it is not very hard. Let me show you how to get the component testing code coverage report for a small example application.

🎁 You can find the complete source code at bahmutov/stub-react-import, specifically in the pull request #1.

Instrument the source files

To collect the code coverage information, we need to instrument the application. When the component tests execute, the counters will be updated, and we will know which source statements were covered by the tests. To insert the counters into the source code, we add the babel-plugin-istanbul into our Babel transpile step.

1
2
$ npm i -D babel-plugin-istanbul
+ [email protected]
cypress.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const { defineConfig } = require('cypress')

module.exports = defineConfig({
component: {
devServer: {
framework: 'create-react-app',
bundler: 'webpack',
webpackConfig: {
mode: 'development',
devtool: false,
module: {
rules: [
// application and Cypress files are bundled like React components
// and instrumented using the babel-plugin-istanbul
// (we will filter the code coverage for non-application files later)
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: [
// we could optionally insert this plugin
// only if the code coverage flag is on
'istanbul',
],
},
},
},
],
},
},
},
},
})

If you execute the component tests now, you can find the window.__coverage__ object that has the counters for each transpiled source file, which includes the application source files and the spec files.

The component source files were instrumented

At the end of the test run, we want to generate the code coverage report based on the window.__coverage__ object.

The code coverage report

To generate the code coverage reports in different formats, including the human-readable HTML format, let's use my plugin @bahmutov/cypress-code-coverage.

1
2
$ npm i -D @bahmutov/cypress-code-coverage
+ @bahmutov/[email protected]

This plugin needs to be included in the cypress.config.js file and in the component support file.

cypress.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const { defineConfig } = require('cypress')

module.exports = defineConfig({
component: {
setupNodeEvents(on, config) {
console.log('setupNodeEvents for components')

// https://github.com/bahmutov/cypress-code-coverage
require('@bahmutov/cypress-code-coverage/plugin')(on, config)

return config
},
devServer: {
// our Webpack setup
}
}
})
cypress/support/component.js
1
2
// https://github.com/bahmutov/cypress-code-coverage
import '@bahmutov/cypress-code-coverage/support'

Great, let's see what we get now. We can see the messages from the code coverage reporting plugin. There are no warnings, so it seems, it is working.

The code coverage plugin is working

Note: the @bahmutov/cypress-code-coverage plugin is a fork of @cypress/code-coverage plugin I have written a while ago. I prefer using my fork, since I have fixed some problems there.

After the test finishes, we can find the generated code coverage reports in the folder coverage. Let's open the HTML report

1
$ open coverage/lcov-report/index.html

The HTML code coverage summary

Hmm, the report includes our test folder cypress/support. What about the src folder?

The code coverage includes the test files

We have a problem. We have files we do not want in the final report, and it is also missing some of the files we do want there. For example, the final report should tell us that the file src/index.js has NOT been covered by the component test (since we have not loaded it)

The source files in our src folder

Include and exclude files

We want our code coverage report to skip the Cypress' own spec files, and include the application's source files. Under the hood, the @bahmutov/cypress-code-coverage plugin is using nyc CLI utility to produce the reports. We can control the source files included in the report by adding the nyc object of settings to the package.json file.

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"nyc": {
"all": true,
"excludeAfterRemap": true,
"include": "src/**/*.js",
"exclude": [
"cypress/**/*.js",
"src/**/*.cy.js",
"src/**/*.test.js",
"src/setupTests.js"
]
}
}

Tip: restart Cypress completely after changing the nyc settings, since we want to reload the package.json file to make the changes take effect. Let's look at our report now:

The code coverage report after excluding the specs and extra files

Nice - the folder cypress is gone, and the only files we got the code coverage for are the real source files in the src folder. The component App.js has been tested by the App.cy.js component test at 100%. The Location.js has been mostly skipped - because we are stubbing its export for the test, read the blog post Stub an import from a Cypress v10 component test.

The code coverage report for the source file Location.js

Finally, the source file index.js has zero code coverage, because the component test never loaded it. We can either add another component test, or test the index.js using an end-to-end test.