Running unit & integration tests with Cypress in a TypeScript React project based on create-react-app.
This project is an example React application that uses
Cypress Component Testing for the organization, writing and execution
of unit / integration tests. You can clone it and play around with it (see Commands). The following sub-chapters explain
how to setup Cypress Component Testing in a create-react-app project, including code coverage output and support for the
Testing Library.
First of all, we need a few new dependencies. In particular:
- Cypress itself (
cypress) - Compatibility with Create React App (
@cypress/react,@cypress/webpack-dev-server,html-webpack-plugin) - Support for code coverage (
@cypress/code-coverage,@cypress/instrument-cra)
Add all those dependencies to your package.json file, remove all Jest-related dependencies, and re-install them. For example:
{
"devDependencies": {
- "@testing-library/jest-dom": "5.14.x",
- "@testing-library/react": "12.0.x",
- "@testing-library/user-event": "13.2.x",
- "@types/jest": "26.0.x",
+ "@cypress/code-coverage": "3.9.x",
+ "@cypress/instrument-cra": "1.4.x",
+ "@cypress/react": "5.9.x",
+ "@cypress/webpack-dev-server": "1.5.x",
+ "cypress": "8.3.x",
+ "html-webpack-plugin": "4.5.x",
}
}You might find an ESLint configuration in your package.json file. If so, remove any Jest-related options from it. For example:
"eslintConfig": {
"extends": [
"react-app",
- "react-app/jest"
]
},In addition, the setupTests.ts file within the src folder is also no longer required and can be deleted. For example:
- // jest-dom adds custom jest matchers for asserting on DOM nodes.
- // allows you to do things like:
- // expect(element).toHaveTextContent(/react/i)
- // learn more: https://github.com/testing-library/jest-dom
- import '@testing-library/jest-dom';First, let's change our test scripts to use Cypress instead of Jest. Within the root folder, update your package.json file:
{
"scripts": {
- "test": "react-scripts test",
+ "test": "cypress run-ct"
+ "test:runner": "cypress open-ct"
}
}In detail:
- The
testscript executes all tests in headless mode - optimal for CI systems - The
test:runnerscript opens up the Cypress Test Runner and let's you choose specific tests to run - perfect for local development and debugging
Note: Watching test files and re-executing tests only works with the
test:runnerscript, and is enabeld by default (cypress#3665).
Now, we want to enable type safety & type support for our Cypress tests. Within the root folder, extend your tsconfig.json file:
{
"compilerOptions": {
+ "types": ["cypress"]
}
}First, we need to do some basic Cypress configuration, such as where to find unit / integration tests or how to run them.
Within the root folder, create a file named cypress.json and add the following content:
+ {
+ "testFiles": "**/*.test.{ts,tsx}",
+ "componentFolder": "src",
+ "video": false
+ }Then, we need to configure Cypress to use the same dev server (Webpack) configuration that Create React App uses, and also to collect and save code coverage.
Within the folder cypress/plugins, create a file named index.ts and add the following content:
+ /// <reference types="cypress" />
+
+ import injectDevServer from '@cypress/react/plugins/react-scripts';
+ import installCoverageTask from '@cypress/code-coverage/task';
+ import '@cypress/instrument-cra';
+
+ const pluginConfig: Cypress.PluginConfig = (on, config) => {
+ if (config.testingType === 'component') {
+ injectDevServer(on, config);
+ }
+ installCoverageTask(on, config);
+ return config;
+ };
+
+ export default pluginConfig;Within the folder cypress/support, create a file named index.ts and add the following content:
+ /// <reference types="cypress" />
+
+ import '@cypress/code-coverage/support'Cypress has its own directory structure and output formats. Within your root folder, extend the .gitignore to exclude them:
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
+ /.nyc_output
+ cypress/results/*
+ cypress/reports/*
+ cypress/screenshots/*
+ cypress/videos/*
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*Mounting test setups and asserting expectations works similarly between Cypress and Jest, but uses a different syntax. For example, the
App.test.tsx requires the following changes:
- import { render, screen } from '@testing-library/react';
+ import { mount } from '@cypress/react'
import App from './App';
- test('renders learn react link', () => {
+ it('renders learn react link', () => {
- render(<App />);
+ mount(<App >);
- const linkElement = screen.getByText(/learn react/i);
+ cy.get('a').should('exist');
});Bonus: How to use the Testing Library
The Testing Library is very popuplar within the React community, and these days is also available for various other frameworks and libraries - amongst them Cypress. The following sub-chapters explain how to set it all up.
Add the dependency to your package.json file and install it. For example:
{
"devDependencies": {
+ "@testing-library/cypress": "8.0.x",
}
}Within the root folder, extend your tsconfig.json file:
{
"compilerOptions": {
- "types": ["cypress"]
+ "types": ["cypress", "@testing-library/cypress"]
}
}Within the folder cypress/support, open the index.ts file and add Testing Library commands:
/// <reference types="cypress" />
import '@cypress/code-coverage/support'
+ import '@testing-library/cypress/add-commands';You can now switch from the Cypress queries to Testing Library ones. For example, the App.test.tsx can now be changed the following way:
import { screen } from '@testing-library/react';
import { mount } from '@cypress/react'
import App from './App';
it('renders learn react link', () => {
mount(<App >);
- cy.get('a').should('exist');
+ cy.findByText(/learn react/i).should('exist');
});The following commands are available:
| Command | Description | CI |
|---|---|---|
npm start |
Creates a development build, running in watch mode | |
npm run build |
Creates a production build | ✔️ |
npm run test |
Executes all unit tests | ✔️ |
npm run test:runner |
Opens the test runner, allowing for specific unit test executions |