Photo by Fili Santillán on Unsplash

How to test a React application

Patrick Lafrance
Workleap
Published in
5 min readJun 22, 2022

--

There are 3 prominent types of tests for Web applications: unit, snapshot and e2e tests. All of them are usually impractical for most applications since they rely on specific DOM selectors and hardcoded identifiers which changes very often when scoped to a whole application due to the volatility of most frontend codebase. At an application scope, a test suite tends to be fragile.

Take a test suite validating the specifications of a user flow with an e2e test runner like Cypress or Playwright. For most applications, depending on how users are authenticated, the steps would be to:

  1. Automate the authentication flow to log into the application
  2. Fake the data associated with the authenticated user to fulfil the requirements of the user flow under test
  3. Redirect the user to the landing page after login
  4. Fake the data fetched by the authenticated landing page to render in a state allowing the activation of the user flow under test
  5. Select and interact with specific DOM elements on the page to start the user flow under test
  6. Go through all the steps of the user flow by selecting and interacting with specific DOM elements
  7. Assert the user flow match the specifications

To succeed, most of these steps rely on the availability of specific DOM structures and hardcoded identifiers which could change frequently. It’s a brittle test suite.

Why are these types of tests so popular then?

Testing at component scope

Once all the parts composing a frontend page of an application are assembled, many more technical and domain-specific concerns have to be taken into account to write good tests. Code volatility becomes more of an issue since there are additional parts under test. Unit, snapshot and e2e tests are suddenly harder to write and easier to breaks.

Why are they popular then?

Because these types of tests can be applied at a component scope rather than an application scope. By doing so, the concerns and the boundaries are split into smaller units, resulting in tests that are easy to write, less prone to volatility and offer great coverage.

Since most React applications are composed of multiple relatively small components, good coverage can be achieved by testing components in an isolated environment.

Testing at component scope has been a popular approach for a while in the design system space and with the help of tools like Storybook, has now matured enough to also be of great help in the application space.

Many components such as a page layout, a custom menu, a custom switch, a part of a page or even a whole page could be tested in isolation. The Guardian Website Storybook has great examples of these.

To start testing at the component scope, set up an isolated environment to develop components with Storybook, the leading platform for component development and start adding visual regression tests, interaction tests and accessibility tests.

Storybook visual regression tests (a mix of snapshot tests and e2e tests)

When combined with Storybook and Chromatic, visual regression tests should be preferred when possible since they are a more effective type of tests. They are easy to write, very similar to what users will experience and won’t break if a DOM structure mutation doesn’t cause any visible change.

Create a Storybook story for each state of the components to test. Each of these stories will act as a test specification. With Storybook Visual tests (and Chromatic) a story can assert that nothing has visually changed between the runs. These tests can be executed from a local environment or on a CI/CD.

Snapshot tests

If the project can’t afford Chromatic, setup Storybook Snapshot tests instead. Tests will break more often since they will rely on the DOM structure of the component rather than screenshots but a snapshot can automatically be updated by accepting the new baseline. It’s faster than manually asserting the values of specific DOM elements of the component.

Interactive states

Regardless of using Chromatic or Snapshots; a play function can be added if a component requires additional setup to reach the state under test, like validating how a component render on hover. This is a very useful approach to setup interactive states rather than leaking an API only for testing purposes.

Storybook interaction tests (unit tests)

When applied to components, unit tests are usually for testing components interactions, e.g., “when a user clicks on an off switch, the switch should turn on”.

Such tests will take longer to write than visual regression tests and require maintenance more often but will execute faster and at no cost.

Improved debugging experience

The usual stack to write interaction tests for React components is a combination of Jest and React Testing library.

It’s a great stack and does work greats but has a major caveat, it’s hard to debug since tests are run through a CLI against a virtual DOM called jsdom. With Storybook, the same Jest + React Testing library stack is used but the tests are executed against a real browser when developing locally.

Debugging tests locally in a real browser and running tests against a virtual DOM on a CI/CD is a great development experience.

Play function

To use Storybook interaction tests, setup a story for each test case. To those tests, add a play function defining the interactions to perform and the assertions. Execute the tests manually from the Interactions addon panel in Storybook or run all the tests at once using the CLI.

A Storybook interaction tests have 3 phases:

  • Setup: define the story, mock the component props & data (you can use Storybook loaders to mock asynchronous data)
  • Execute: render the story in isolation (the play function)
  • Verify: execute the assertion (contained in the play function)

Storybook accessibility tests

Storybook also makes it easy to test Accessibility requirements. Storybook integrates with Axe accessibility linter to enable accessibility tests locally in the Accessibility addon panel or on your CI/CD with the CLI.

Non component stuff

Some application could have utility functions, hooks or even some complex business logic to test (though, business logic should almost always be kept in the backend). Those tests should be written with Jest (and React Testing library for hooks) directly rather than Storybook.

A note about e2e testing

Component testing cover individual parts of an application and doesn’t guarantee like e2e tests that the features will behave as expected once the parts are integrated. Therefore, you should do a minimal coverage with manual e2e tests for the areas affected by a change.

Resources

Learn more about testing at a component scope with Storybook:

--

--