Tutorial

How To Write Snapshot Tests For React Components With Jest

DevelopmentJavaScriptReact

Introduction

Snapshot testing allows you to ensure your output continues to behave as expected. This is useful because as you revisit your code to make updates over time, there is an increased likelihood that those changes may cause something to break.

Unlike strict Test Driven Development (TDD), where the standard practice is to write failing tests first then write the code to make the tests pass, snapshot testing takes a different approach.

When writing snapshot tests for a React component, you first need to have code in a working state. Then, generate a snapshot of its expected output given certain data. The snapshot tests are committed alongside the component. Jest, a testing framework, will compare the snapshot to the rendered output for the test.

In the event of a failed test, it can mean two things. If the test results are unexpected, you may need to address an issue with your component. If the test results are expected, it may mean that the snapshot tests need to be updated to support the new output.

In this tutorial, you will be exploring snapshot tests and how you can use them to ensure your user interface (UI) does not change unexpectedly.

Prerequisites

To complete this tutorial, you will need:

This tutorial also utilizes Visual Studio Code as a code editor and for the convenience of running an integrated terminal. However, you can replace this with the editor and terminal of your choice.

This tutorial was verified with Node v14.7.0, npm v6.14.7, react v16.13.1, and jest v24.9.0.

Step 1 — Creating a React Component to Test

First, in order to have something to test, you will need to create a React App using Create React App. For this tutorial, the project will be called react-snapshot-tests.

Open your terminal and run the following command:

  • npx create-react-app@3.4.1 react-snapshot-tests

Then, change into the newly created app directory:

  • cd react-snapshot-tests

Next, start the app:

  • npm start

At this point, you should now have a React App running and can view it in a web browser. Next, you will need to create a component that you can test.

For the purposes of this tutorial, the component that you are going to be creating renders the items props it receives.

In your terminal, make a components subdirectory under src:

  • mkdir src/components

Then, create an Items.js component:

  • nano src/components/Items.js

Add the following code to Items.js:

src/components/Items.js
import React from 'react';
import PropTypes from 'prop-types';

/**
 * Render a list of items
 *
 * @param {Object} props - List of items
 */
function Items(props) {
  const { items = [] } = props;

  // A single item in the list, render a span.
  if (items.length === 1) {
    return <span>{items[0]}</span>;
  }

  // Multiple items on the list, render a list.
  if (items.length > 1) {
    return (
      <ul>
        {items.map(item => <li key={item}>{item}</li>)}
      </ul>
    );
  }

  // No items on the list, render an empty message.
  return <span>No items in list</span>;
}

Items.propTypes = {
  items: PropTypes.array,
};

Items.defaultProps = {
  items: []
};

export default Items;

This code will render the items props based on the amount:

  • If there are multiple items, the items will be displayed in an unordered list (<ul>).
  • If there is a single item, the item will be displayed in a <span> element.
  • if there are no items, an error message will be displayed.

Finally, update App.js to render our component:

  • nano src/App.js

Replace the contents of App.js with the following:

src/App.js
import React, { Component } from 'react';
import Items from './components/Items';

class App extends Component {
  render() {
    const items = [
      'Shark',
      'Dolphin',
      'Octopus'
    ];
    return (
      <Items items={items} />
    );
  }
}

export default App;

If you visit the app in the browser, there will be a screen with a list of the values you established in App.js:

Output
* Shark * Dolphin * Octopus

Since there were multiple items, it is displayed as an unordered list.

Next, you will add your snapshot tests.

Step 2 — Writing Snapshot Tests

To get started, delete the App.test.js file that was generated by Create React App:

  • rm src/App.test.js

It will not be required for this tutorial.

Next, install react-test-renderer, a library that enables you to render React components as JavaScript objects without the need for a DOM.

  • npm install react-test-renderer@16.13.1

Let’s add your first test. To get started, you will create an Items.test.js file:

  • nano src/components/Items.test.js

Write a test that renders the Items component with no items passed down as props:

src/components/Items.test.js
import React from 'react';
import renderer from 'react-test-renderer';

import Items from './Items';

it('renders correctly when there are no items', () => {
  const tree = renderer.create(<Items />).toJSON();
  expect(tree).toMatchSnapshot();
});

Next, let’s run the tests. Create React App handled all the initialization for setting up tests:

  • npm test

You should get a passing test for "renders correctly when there are no items":

A passing test!

When you run a snapshot test for the first time, notice that a new snapshot file is created inside a __snapshots__ directory. Since your test file is named Items.test.js, the snapshot file is appropriately named Items.test.js.snap.

The contents of Items.tests.js.snap should resemble:

src/components/__snapshots__/Items.test.js.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly when there are no items 1`] = `
<span>
  No items in list
</span>
`;

This snapshot matches the component’s exact output.

Jest uses pretty-format to make the snapshot files human-readable.

You can now create the tests for the two other scenarios where there is one item and where there are multiple items.

Open Items.tests.js:

  • nano src/components/Items.test.js

Add the following lines of code:

src/components/Items.test.js
// ...

it('renders correctly when there is a single item', () => {
  const items = ['one'];
  const tree = renderer.create(<Items items={items} />).toJSON();
  expect(tree).toMatchSnapshot();
});

it('renders correctly when there are multiple items', () => {
  const items = ['one', 'two', 'three'];
  const tree = renderer.create(<Items items={items} />).toJSON();
  expect(tree).toMatchSnapshot();
});

At this point, you have three tests written: one for no items, one for a single item, and another for multiple items.

Rerun the tests:

  • npm test

All three tests should pass successfully, and you will now have three snapshots in your __snapshots__ directory.

Next, you will address a failing test by updating a snapshot test.

Step 3 — Updating Snapshot Tests

To better understand why you need snapshot tests, you will introduce changes to the Items component and re-run the tests. This will represent a simulation of what would happen when changes are made to a project in development.

Open Items.js:

  • nano src/components/Items.js

Add class names to the span and li elements:

src/components/Items.js
...
/**
 * Render a list of items
 *
 * @param {Object} props - List of items
 */
function Items(props) {
  const { items = [] } = props;

  // A single item in the list, render a span.
  if (items.length === 1) {
    return <span className="item-message">{items[0]}</span>;
  }

  // Multiple items on the list, render a list.
  if (items.length > 1) {
    return (
      <ul>
        {items.map(item => <li key={item} className="item-message">{item}</li>)}
      </ul>
    );
  }

  // No items on the list, render an empty message.
  return <span className="empty-message">No items in list</span>;
}

Items.propTypes = {
  items: PropTypes.array,
};

Items.defaultProps = {
  items: [],
};

export default Items;

Let’s rerun the tests:

  • npm test

You will observe failing test results:

Failing tests

Jest matched the existing snapshots against the rendered component with the updated changes and failed because there were some additions to your component. It then shows a diff of the changes that are introduced to the snapshot tests.

If the changes are not expected, you caught the error before the change was deployed and can now address the error. If the changes were expected, you would need to update your snapshot tests to get them to pass correctly.

For the tutorial, you can assume that this was an expected change. You intended to add class names to the component. You should then update the snapshot tests.

While Jest is in interactive mode, you can update the snapshot tests by pressing u with the options provided:

Watch Mode Options

Note: Alternatively, if you have Jest installed globally, you can run jest --updateSnapshot or jest -u.

This will update the snapshots to match the updates you made, and your tests will pass.

Here is the previous snapshot test for no items:

src/components/__snapshots__/Items.test.js.snap
//  ...

exports[`renders correctly when there are no items 1`] = `
<span>
  No items in list
</span>
`;

//  ...

And here is the newly updated snapshot test for no items:

src/components/__snapshots__/Items.test.js.snap
//  ...

exports[`renders correctly when there are no items 1`] = `
<span
  className="empty-message"
>
  No items in list
</span>
`;

// ...

After updating the tests, they will pass:

Passing tests

You now have passing tests again. If this was a project in development, you could deploy the code knowing the changes you intended are documented for future development.

Conclusion

In this tutorial, you wrote snapshot tests for a React component. You also modified the component to experience failing tests. Finally, you updated the snapshots to fix the tests.

This was a small simulation of a live project. This cycle of tests passing, failing, and addressing the failures will be part of your development workflow.

Snapshot testing is meant to be one of many different testing tools. Therefore, you may still need to write tests for your actions and reducers.

While you have explored the basics of snapshot tests, there is a lot you can learn about writing better snapshot tests. Do take a look at the Snapshot best practices from the Jest’s documentation to learn more about snapshot testing.

If you’d like to learn more about React, check out our React topic page for exercises and programming projects.

Creative Commons License