Jest with Cypress coverage for full-stack web applications

Feb 26, 2020Testinghttps://git.io/JvuSN

Context

As concluded by the video in Code Coverage chapter of Cypress Guides, end-to-end test can be much more efficient than unit test, because we can achieve higher coverage with less test cases and less mocks. So the suggested way of testing is to start with e2e test to get a decent coverage percentage, and add some unit test to cover the code that is too hard for e2e test to reach. This way we can get near 100% coverage for our web applications without much pain.

While Cypress supports unit test itself, but personally I prefer the mock functions and matchers of Jest. So here I'm going to share the steps of combining Jest with Cypress and how to merge the coverage. Most of the steps are inspired by bahmutov/cypress-and-jest but this post is trying to dive deeper and cover the use case for full-stack web application scenarios.

Install extra dependencies

npm i -D @cypress/code-coverage nyc istanbul-lib-coverage babel-plugin-istanbul
  • @cypress/code-coverage: Cypress plugin for collecting coverage during Cypress testing.
  • nyc: Help to run both Cypress test suite and server side code with instrumentation.
  • istanbul-lib-coverage: peer dependency of @cypress/code-coverage, provide API to merge coverages of all test suites.
  • babel-plugin-istanbul: Instrument server side code for code coverage.

Enable @cypress/code-coverage plugin

Add support.js to cypress/support/index.js:

import '@cypress/code-coverage/support';

Register task and use-babelrc hooks in cypress/plugins/index.js:

module.exports = (on, config) => {
  on('task', require('@cypress/code-coverage/task'));
  on('file:preprocessor', require('@cypress/code-coverage/use-babelrc'));
}

Add babel-plugin-istanbul plugin

Since @cypress/code-coverage plugin does not instrument the code, so babel-plugin-istanbul need to be added to .babelrc. However, Jest wraps Istanbul and istanbul plugin is loaded automatically by jest-cli. Babel does not allow duplicate plugin so simply adding istanbul plugin to .babelrc will lead to Duplicate plugin/preset detected. error when running Jest testing.

So one quick solution is to add the istanbul plugin to a different env other than test since Jest load test env by default. One example is e2e. Here's a final example of .babelrc:

{
  "env": {
    "test": {
      "plugins": []
    },
    "e2e": {
      "plugins": ["istanbul"]
    }
  }
}

Configure nyc

@cypress/code-coverage plugin runs nyc internally (as defined in the task.js), but part of the nyc options are passed by cli options read from package.json. So different types for configuration files are not supported perfectly. So the best place for nyc configuration is in the package.json.

Here's a example of package.json:

{
  "nyc": {
    "all": true,
    "include": ["src/**"],
    "exclude": [],
    "reporter": []
  },
  "scripts": {
    "start": "node src/server.js",
    "start:coverage": "NODE_ENV=e2e nyc --silent npm start"
  }
}

Here the reporter option is set as empty array, because we only want coverage to be collected (in the temporary directory, default path is .nyc_output), reporting is to be done later when merge with Jest coverage.

The start:coverage is to start the server by nyc, and the NODE_ENV=e2e is to load the babel-plugin-istanbul for server side code instrument.

Configure Jest

Add option in jest.config.js:

module.exports = {
  collectCoverage: true,
  coverageReporters: ['json'],
};

Here the coverageReporters is set to json. Similar to the nyc config, other types of reporters are useless for now. So only JSON format report is set.

Merge coverage

Add few more scripts in package.json:

{
  "scripts": {
    "precover:merge": "mv coverage/coverage-final.json .nyc_output",
    "cover:merge": "nyc merge .nyc_output coverage/coverage.json",
    "cover:report": "nyc report --reporter=lcov --reporter=text-summary"
  }
}
  • precover:merge: The coverage/coverage-final.json is the report from Jest, move it to nyc temporary directory.
  • cover:merge: Merge coverage collected by Cypress and Jest in .nyc_output and output to coverage/coverage.json.
  • cover:report: Generate report based on coverage/coverage.json, reporter format is lcov and text-summary.

Powered by Gatsby. Theme inspired by end2end.

© 2014-2020. Made with by mdluo.