Unit testing React components in Cypress without CRA and react-scripts
So you have an app or component library that you have created without create-react-app
and you want to unit test it using Cypress. I had to go through some hassle to get that working with a custom webpack configuration, but it’s finally working, and here is my solution.
1. Install Cypress
npm install --save-dev cypress @cypress/react
Additional installation instructions can be found here.
2. Add cypress.json
{
"testFiles": "**/*.spec.js",
"experimentalComponentTesting": true,
"componentFolder": "./src",
"nodeVersion": "system",
"env": {
"webpackFilename": "./webpack.tests.config.js"
}
}
testFiles
specifies a pattern for files that contain your testsexperimentalComponentTesting
enables unit testing support. Though it says experimental, it’s quite stable nowcomponentFolder
is akinintegrationFolder
and specifies where your unit and integration tests reside. You could keep your unit tests inside your./src
folder and have one to one mapping to your components, i.e.src/Button/Button.js
andsrc/Button/Button.spec.js
nodeVersion
tells Cypress to use the node version of the system it runs on, and not use the bundled version. This might be necessary if you are usingnode-sass
and other binaries that are platform specific.env.webpackFilename
specifies the localtion of the webpack config you want to use for your tests. Cypress will transpile your components on the fly, hence you may need to have a different configuration that what you use to build your project. Think Storybook.
All the configuration options are defined here.
3. Update support bindings
Update ./cypress/support/index.js
and import directives needed for Cypress to mount your unit tests into the Cypress DOM.
// ./cypress/support/index.jsimport '@cypress/react/support';
4. Configure the plugins
Update ./cypress/plugins/index.js
and tell Cypress how to process your test files.
// ./cypress/plugins/index.jsmodule.exports = (on, config) => {
require('@cypress/react/plugins/load-webpack')(on, config); return config;
};
load-webpack
plugin will transpile your components files during testing using webpack config you have specified in env.webpackFilename
.
5. Write your tests
// ./src/Button.spec.jsimport React from 'react';
import Button from './Button';
import { mount } from '@cypress/react';describe('Button', () => {
it('should handle onClick', () => {
const onClick = cy.stub().as('onClick');
mount(
<Button onClick={onClick}>Click me</Button>,
);
cy.findByRole('button', { name: 'Click me' }).click();
cy.get('@onClick').should('have.been.calledOnce');
});
});
6. Run your tests
You can now run your tests with ./node_modules/.bin/cypress open
or ./node_modules/.bin/cypress run
.
7. Rejoice
Running components tests in an actual DOM boosts confidence in the reliability of your tests. You can run them in multiple browsers. You can create functional tests combining multiple components together to see how they interact with each other, how user interacts with them using the mouse and the keyboard, and you can stub your dependencies to avoid complex cy.intercept
definitions.
And added bonus, is that Cypress automatically generates test coverage reports, so you don’t have to instrument anything.