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 |