brian-mann / cypress-react-unit-test

Unit test React components using Cypress

Home Page:https://glebbahmutov.com/blog/my-vision-for-component-tests/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

cypress-react-unit-test CircleCI Cypress.io tests renovate-app badge

A little helper to unit test React components in the open source Cypress.io E2E test runner v4.5.0+

Jump to: Comparison, Blog posts, Install, Examples: basic, advanced, full, external, Style options, Code coverage, Visual testing, Common problems, Chat

Survey

Hi there! We are trying to collect feedback from Cypress users who need component testing. Answer a few questions in this survey about component testing to help us πŸ™

TLDR

  • What is this? This package allows you to use Cypress test runner to unit test your React components with zero effort. Here is a typical component testing, notice there is not external URL shown, since it is mounting the component directly.

Example component test

Comparison

Feature Jest / Enzyme / RTL Cypress + cypress-react-unit-test
Test runs in real browser ❌ βœ…
Supports shallow mount βœ… ❌
Supports full mount βœ… βœ…
Test speed 🏎 as fast as the app works in the browser
Test can use additional plugins maybe use any Cypress plugin
Test can interact with component synthetic limited API use any Cypress command
Test can be debugged via terminal and Node debugger use browser DevTools
Built-in time traveling debugger ❌ Cypress time traveling debugger
Re-run tests on file or test change βœ… βœ…
Test output on CI terminal terminal, screenshots, videos
Tests can be run in parallel βœ… βœ… via parallelization
Test against interface if using @testing-library/react βœ… and can use @testing-library/cypress
Spying and stubbing methods Jest mocks Sinon library
Stubbing imports βœ… βœ…
Stubbing clock βœ… βœ…
Code coverage βœ… βœ…

If you are coming from Jest + RTL world, read Test The Interface Not The Implementation.

If you are coming from Enzyme world, check out the enzyme example.

Blog posts

Known problems

See issues labeled v4

Install

Requires Node version 8 or above.

npm install --save-dev cypress cypress-react-unit-test
  1. Include this plugin from your project's cypress/support/index.js
require('cypress-react-unit-test/support')
  1. Tell Cypress how your React application is transpiled or bundled (using Webpack), so Cypress can load your components. For example, if you use react-scripts (even after ejecting) do:
// cypress/plugins/index.js
module.exports = (on, config) => {
  require('cypress-react-unit-test/plugins/react-scripts')(on, config)
  // IMPORTANT to return the config object
  // with the any changed environment variables
  return config
}

See Recipes for more examples.

  1. ⚠️ Turn the experimental component support on in your cypress.json. You can also specify where component spec files are located. For example, to have them located in src folder use:
{
  "experimentalComponentTesting": true,
  "componentFolder": "src"
}

API

  • mount is the most important function, allows to mount a given React component as a mini web application and interact with it using Cypress commands
  • createMount factory function that creates new mount function with default options
  • unmount removes previously mounted component, mostly useful to test how the component cleans up after itself
  • mountHook mounts a given React Hook in a test component for full testing, see hooks example

Examples

import React from 'react'
import { mount } from 'cypress-react-unit-test'
import { HelloWorld } from './hello-world.jsx'
describe('HelloWorld component', () => {
  it('works', () => {
    mount(<HelloWorld />)
    // now use standard Cypress commands
    cy.contains('Hello World!').should('be.visible')
  })
})

Look at the examples in cypress/component folder. Here is the list of examples showing various testing scenarios.

Basic examples

Spec Description
alias Retrieve mounted component by its name or alias
alert-spec.js Component tries to use window.alert
counter-set-state Counter component that uses this.state
counter-use-hooks Counter component that uses useState hook
document-spec Checks document dimensions from the component
enzyme Several specs showing how to recreate Enzyme's setProps, setState, and setContext methods.
emotion-spec.js Confirms the component is using @emotion/core and styles are set
error-boundary-spec.js Checks if an error boundary component works
pure-component-spec.js Tests stateless component
stateless-spec.js Passes Cypress stub to the component, confirms the component calls it on click
window-spec.js In the component test, the spec window and the application's window where the component is running should be the same object
css Shows that component with import './Button.css' works
css modules Shows that component that using css modules styles works
network Confirms we can use cy.route to stub / spy on component's network calls
no-visit Component specs cannot call cy.visit
re-render Checking how the component re-renders when its props change
react-book-by-chris-noring Copied test examples from React Book and adapted for Cypress component tests
react-tutorial Tests from official ReactJS tutorial copied and adapted for Cypress component tests
stub-example Uses cy.stub as component props
styles Add extra styles to the component during testing using style, cssFile or stylesheets mount options
toggle-example Testing a toggle component using Cypress DOM commands
typescript A spec written in TypeScript
unmount Verifies the component's behavior when it is unmounted from the DOM
use-lodash-fp Imports and tests methods from lodash/fp dependency
styled-components Test components that use styled-components

plus a few smaller sanity specs in cypress/component/basic folder.

Advanced examples

Spec Description
api-test Mix REST api tests that use cy-api with component tests
app-action-example App actions against components
context Confirms components that use React context feature work
custom-command Wraps mount in a custom command for convenience
forward-ref Tests a component that uses a forward ref feature
hooks Tests several components that use React Hooks like useState, useCallback
lazy-loaded Confirms components that use React.lazy and dynamic imports work
material-ui-example Large components demos from Material UI
mock-fetch Test stubs window.fetch used by component in useEffect hook
mocking-axios Stubbing methods from a 3rd party component like axios
mocking-component Replaced a child component with dummy component during test
mocking-imports Stub a named ES6 import in various situations
react-router-v6 Example testing a React Router v6. Both browser and in memory routers
renderless Testing a component that does not need to render itself into the DOM
set-timeout-example Control the clock with cy.tick and test loading components that use setTimeout
test-retries This component is compatible with Cypress Test Retries
testing-lib-example A spec adopted from @testing-library/react that uses @testing-library/cypress
timers Testing components that set timers, adopted from ReactJS Testing recipes
tutorial A few tests adopted from ReactJS Tutorial, including Tic-Tac-Toe game
use-local-storage Use hooks to load and save items into localStorage
portal Component test for ReactDOM.createPortal feature
radioactive-state Testing components that use radioactive-state library
react-bootstrap Confirms react-bootstrap components are working
select React component Uses cypress-react-selector to find DOM elements using React component name and state values
lazy-loaded Uses multiple chunks and async components with React.lazy + React.Suspense.
i18n Usesreact-i18next for localizaiton.
framer-motion Uses framer motion for javascript-based animation.

Full examples

We have several subfolders in examples folder that have complete projects with just their dependencies installed in the root folder.

Folder Name Description
a11y Testing components' accessibility using cypress-axe
react-scripts A project using react-scripts with component tests in src folder, including the .env files demo.
react-scripts-folder A project using react-scripts with component tests in cypress/component
tailwind Testing styles built using Tailwind CSS
sass-and-ts Example with Webpack, Sass and TypeScript
snapshots Component HTML and JSON snapshots using cypress-plugin-snapshots
visual-sudoku Visual testing for components using open source plugin cypress-image-snapshot. For larger example with an hour long list of explanation videos, see bahmutov/sudoku.
visual-testing-with-percy Visual testing for components using 3rd party service Percy.io
visual-testing-with-happo Visual testing for components using 3rd party service Happo
visual-testing-with-applitools Visual testing for components using 3rd party service Applitools.com
using-babel Bundling specs and loaded source files using project's existing .babelrc file
webpack-file Load existing webpack.config.js file
webpack-options Using the default Webpack options from @cypress/webpack-preprocessor to transpile JSX specs

External examples

This way of component testing has been verified in a number of forked 3rd party projects.

Repo Description
try-cra-with-unit-test Hello world initialized with CRAv3
try-cra-app-typescript Hello world initialized with CRAv3 --typescript
react-todo-with-hooks Modern web application using hooks
test-redux-examples Example apps copies from official Redux repo and tested as components
test-react-hooks-animations Testing React springs fun blob animation
test-mdx-example Example testing MDX components using Cypress
test-apollo Component testing an application that uses Apollo GraphQL library
test-xstate-react XState component testing using Cypress
test-react-router-v5 A few tests of React Router v5
test-material-ui Testing Material UI components: date pickers, lists, autocomplete
test-d3-react-gauge Testing React D3 gauges
storybook-code-coverage Example app where we get 100% code coverage easily with a single integration spec and a few component specs, replacing several tools
react-loading-skeleton One to one Storybook tests for React skeleton components. Uses local .babelrc settings without Webpack config
test-swr Component test for Zeit SWR hooks for remote data fetching
emoji-search Quick component test for a fork of emoji-search
test-custom-error-boundary Play with a component that implements error boundary
Jscrambler-Webpack-React Example project with its own Webpack config file
bahmutov/integration-tests Example based on blog post React Integration Testing: Greater Coverage, Fewer Tests
mobx-react-typescript-boilerplate Fork of the official Mobx example, shows clock control
bahmutov/test-react-hook-form Testing forms created using react-hook-form
bahmutov/react-with-rollup Testing a React application bundled with Rollup by using @bahmutov/cy-rollup preprocessor
bahmutov/testing-react-example Described in blog post Test React Component with cypress-react-unit-test Example
ejected-react-scripts-example Using component testing after ejecting react-scripts
tic-tac-toe Component and unit tests for Tic-Tac-Toe, read Tic-Tac-Toe Component Tests
react-hooks-file-upload Upload a file from the component while stubbing the server
react-query-example Quick test example for components that use react-query with mock clock control

To find more examples, see GitHub topic cypress-react-unit-test-example

Options

In most cases, the component already imports its own styles, thus it looks "right" during the test. If you need another CSS, the simplest way is to import it from the spec file:

// src/Footer.spec.js
import './styles/main.css'
import Footer from './Footer'
it('looks right', () => {
  // styles are applied
  mount(<Footer />)
})

Extra styles

You can pass additional styles, css files and external stylesheets to load, see docs/styles.md for the full list of options.

const todo = {
  id: '123',
  title: 'Write more tests',
}
mount(<Todo todo={todo} />, {
  stylesheets: [
    'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css',
  ],
})
Additional configuration If your React and React DOM libraries are installed in non-standard paths (think monorepo scenario), you can tell this plugin where to find them. In `cypress.json` specify paths like this:
{
  "env": {
    "cypress-react-unit-test": {
      "react": "node_modules/react/umd/react.development.js",
      "react-dom": "node_modules/react-dom/umd/react-dom.development.js"
    }
  }
}

Code coverage

If you are using plugins/cra-v3 it instruments the code on the fly using babel-plugin-istanbul and generates report using dependency cypress-io/code-coverage (included). If you want to disable code coverage instrumentation and reporting, use --env coverage=false or CYPRESS_coverage=false or set in your cypress.json file

{
  "env": {
    "coverage": false
  }
}

Visual testing

You can use any Cypress Visual Testing plugin to perform visual testing from the component tests. This repo has several example projects, see visual-sudoku, visual-testing-with-percy, visual-testing-with-happo, and visual-testing-with-applitools.

For a larger Do-It-Yourself example with an hour long list of explanation videos, see bahmutov/sudoku repository. I explain how to write visual testing using open source tools in this blog post, video talk, and slides.

Common problems

Node Sass

When using Node Sass styles, tell Cypress to use the system NodeJS rather than its bundled version. In cypress.json set option:

{
  "nodeVersion": "system"
}

Find full example in sass-and-ts folder.

Slow bundling

When you bundle spec file, you are now bundling React, Read DOM and other libraries, which is might be slow. For now, you can disable inline source maps by adding to your Webpack config settings (if available) the following:

const webpackOptions = {
  devtool: false,
}

Keep your eye on issue #156 for more information.

Missing code coverage

If you are using your custom Webpack, this plugin might be missing code coverage information because the code was not instrumented. We try to insert the babel-plugin-istanbul plugin automatically, but your bundling might not use Babel, or configure it differently, preventing plugin insertion. Please let us know by opening an issue with full reproducible details.

See related issue #141. You can also debug the plugin's behavior by running it with DEBUG environment variable, see #debugging section.

Gatsby.js projects not supported

Currently, this project cannot find Webpack settings used by Gatsby.js, thus it cannot bundle specs and application code correctly. Keep an eye on #307

Chat

We have a chat workspace at https://component-testing.slack.com/, you are welcome to join us.

Development

See docs/development.md

Debugging

You can see verbose logs from this plugin by running with environment variable

DEBUG=cypress-react-unit-test

Because finding and modifying Webpack settings while running this plugin is done by find-webpack module, you might want to enable its debug messages too.

DEBUG=cypress-react-unit-test,find-webpack

Migration guide

From v3 to v4

The old v3 main branch is available as branch v3

  • the cy.mount is now simply import { mount } from 'cypress-react-unit-test'
  • the support file is simply require('cypress-react-unit-test/support')

Related tools

Same feature for unit testing components from other frameworks using Cypress

About

Unit test React components using Cypress

https://glebbahmutov.com/blog/my-vision-for-component-tests/


Languages

Language:JavaScript 84.6%Language:TypeScript 10.8%Language:CSS 4.4%Language:HTML 0.2%