Tutorial

How To Write Snapshot Tests For React Components With Jest

DevelopmentJavaScriptReact

While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

In this tutorial, we will be looking at what snapshot tests are and how we can use snapshot testing to ensure our user interface does not change without the team knowing about it.

To get started, you will need to familiarize yourself with the following:

  1. NodeJS - A JavaScript runtime built on Chrome’s V8 JavaScript engine.
  2. React - A JavaScript library for building delightful UI by Facebook.
  3. Jest - A JavaScript testing framework by Facebook.

What Is Snapshot Testing?

Unlike strict Test Driven Development 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.

To write a snapshot test, you first get your code working, say, a React component, then generate a snapshot of it’s expected output given certain data. The snapshot tests are committed alongside the component and every time the tests are run. Jest will compare the snapshot to the rendered output for the test.

If the test does not pass, it may mean that there were some unexpected changes on the component that you need to fix, or you made some changes to the component and it’s about time you updated the snapshot tests.

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.

Let’s get right into it!

Creating a Simple React Component

To get started, we will create a React App using Create React App.

  • create-react-app ooh-snap
  • cd ooh-snap
  • yarn start

We should now have a React app. Let’s go ahead and create a component that we can test. The component that we are going to be creating renders the items props it receives as either a list or as a span element depending on the number of items.

Create a Components folder then add the following Items component:

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;

  if (!items.length) {
    // No Items on the list, render an empty message
    return <span>No items in list</span>;
  }

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

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

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

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

export default Items;

Finally, let’s update App.js to render our Component:

import React, { Component } from 'react';
import Items from './Components/Items';

class App extends Component {
  render() {
    const items = [
      'Thor',
      'Captain America',
      'Hulk'
    ];
    return (
      <Items items={items} />
    );
  }
}

export default App;

Effectively, delete App.test.js because we will be adding our own tests in the next section.

Next, let’s go ahead and add our snapshot tests.

Writing Snapshot Tests

To get started, install react-test-renderer, a library that enables you to render React components as JavaScript objects without the need of a DOM.

  • yarn add react-test-renderer

Let’s add our first test. To get started, we will create a test that renders the Items component with no items passed down as props:

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. Thanks to Create React App, we do not need to set anything else up to run our tests.

  • yarn test

When you run the tests for the first time, notice that a new snapshot file is created inside a __snapshots__ directory. Since our test file is named Items.test.js, the snapshot file is appropriately named Items.test.js.snap that looks like this:

// Jest Snapshot v1, https://goo.gl/fbAQLP

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

The test matches the component’s exact output. Jest uses pretty-format to make the snapshot files human readable.

If you are getting the hang of it, go ahead and create tests for the two other scenarios where there is one item and where there is multiple items, then run the tests.

...
it('renders correctly when there is one 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();
});

Next, let’s make updates to our component.

Updating Snapshot Tests

To understand why we need snapshot tests, we’ll go ahead and update the Items component and re-run the tests. This, for your dev environment, is a simulation of what would happen when someone on your team makes a change to a component and your CI tool runs the tests.

We will add class names to the span and li elements, say, to affect some styling.

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

  if (!items.length) {
    // No Items on the list, render an empty message
    return <span className="empty-message">No items in list</span>;
  }

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

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

Let’s run our tests again with yarn test command in lieu of our changes.

Failing tests

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

To fix this, for whatever reason, would entirely depend on the changes that were introduced to the snapshot tests.

If the changes are not expected, that’s good, you got it well in advance before it was too late. If the changes were expected, update your snapshot tests and everything is green again.

While Jest is in interactive mode, you can update the snapshot tests by simply pressing u with the options provided. Alternatively, you can run jest --updateSnapshot or jest -u.

Watch Mode Options

This will update the snapshots to match the updates we made and our tests will effectively pass.

Passing tests

Go ahead and pick into the snapshots folder to see how the snapshot files changed. Here is the updated empty snapshot snippet.

exports[`renders correctly when there is one item 1`] = `
<span
  className="item-message"
>
  one
</span>
`;

Conclusion

In this tutorial, we have been able to write snapshot tests for a React component. We also updated the component to see the failing tests and eventually update the snapshots to fix the tests.

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

0 Comments

Creative Commons License