Navigate back to the homepage
BLOG
Try NowLogin
Back

React Snapshot Testing With Jest: An Introduction With Examples

Michiel Mulders
January 29th, 2021 · 5 min read

Testing forms an integral part of software development. Both frontend and backend developers write tests. You can’t escape them. Testing is an excellent way to improve trust in your code but also to guarantee code quality.

Many companies prefer to adopt agile development. Here, testing is part of the iterative development methodology. It allows software teams to catch bugs early on in the software development lifecycle. Catching those bugs early on saves your organization a lot of money. The cost of fixing a bug increases the longer it stays undetected.

This article takes a look at snapshot testing specifically. It’s a technique for testing React components. Snapshot testing ensures that your UI doesn’t change unexpectedly. Further, we’ll be using the Jest CLI tool to maintain and update our snapshot tests.

So, why is snapshot testing such a powerful concept?

Why is snapshot testing so powerful for React component testing?

First of all, it’s essential to understand how snapshot testing works. It’s a type of comparison testing that compares the stored “good output” with the current output of a component. Therefore, snapshot testing is different from unit and functional testing. These types of testing focus on making assertions about the expected behavior or output. Instead, snapshot testing is only concerned with detecting unexpected UI changes. It won’t tell you anything whether the component’s behavior is false or correct. Snapshot testing only tells you that the output is different.

So, why would you use snapshot testing?

Snapshot testing has been created due to the need for an easier way to write tests for React components. Many React developers reported that they spend more time writing tests than the actual component. Therefore, snapshot testing allows React developers to quickly generate tests using its simple syntax.

You can create a snapshot test with just two lines of code. First, you have to pass a component and its specific properties you want to test. By calling the toJSON(), function you can generate an output for your component which you can later compare to detect unexpected UI changes. Next, we can pass the output to the expect() assertion and call the toMatchSnapshot() function to compare the current snapshot with the newly generated output.

Here’s an example.

1const elem = renderer.create(<MyComponent foo="bar">).toJSON();
2expect(elem).toMatchSnapshot();

Note that snapshot testing doesn’t support Test-Driven Development (TDD) as we need a finished component before we can “snapshot” it.

Now, let’s prepare a small project to experiment with snapshot testing.

Project setup: Create a React Component

Let’s create a new React project using create-react-app.

1$ npx create-react-app@3.4.1 react-snapshot-testing

Next, let’s change into the newly created folder.

1$ cd react-snapshot-testing

Now, let’s start the app to check if the setup works.

1$ npm start

This command will start the React app and open the webpage in your browser at http://localhost:3000/. Next, let’s create a React component. In this example, let’s create a simple Books component that renders a list of books. To make this an exciting testing example, let’s render a different UI depending on the length of the passed books array.

In your terminal, make a components directory under the src directory.

1$ mkdir src/components

Further, let’s create a Books.js file inside the components directory.

1$ touch src/components/Books.js

Lastly, add the following code to Books.js:

1import React from 'react';
2import PropTypes from 'prop-types';
3
4/**
5 * Render a list of books
6 *
7 * @param {Object} props - List of books
8 */
9function Books(props) {
10 const { books = [] } = props;
11
12 // A single book in the list, render book in paragraph element
13 if (books.length === 1) {
14 return <p>{books[0]}</p>;
15 }
16
17 // Multiple books on the list, render a list.
18 if (books.length > 1) {
19 return (
20 <ol>
21 {books.map(book => <li key={book}>{book}</li>)}
22 </ol>
23 );
24 }
25
26 // No books on the list, render an empty message.
27 return <span>We didn't find any books.</span>;
28}
29
30Books.propTypes = {
31 books: PropTypes.array,
32};
33
34Books.defaultProps = {
35 books: []
36};
37
38export default Books;

There are three possible outcomes for our Books component: 1. Render a message when there are no books in a <span> tag 2. Render a single book in a <p> tag 3. Render multiple books in a <ol> tag

Finally, we have to update the src/App.js code to render the Books component. Make sure to replace the contents of the App.js file with the code below:

1import React, { Component } from 'react';
2import Books from './components/Books';
3
4class App extends Component {
5 render() {
6 const books = [
7 'Harry Potter',
8 'The Lord of the Rings',
9 'The City of Dreaming Books'
10 ];
11 return (
12 <div className="App">
13 <Books books={books} />
14 </div>
15 );
16 }
17}
18
19export default App;

You can verify if everything works correctly by visiting localhost:3000 in your browser. Make sure the npm start command is still running in your terminal. If yes, your browser renders the following output.

11. Harry Potter
22. The Lord of the Rings
33. The City of Dreaming Books

All good? Let’s prepare the testing setup.

Prepare the testing setup

Firstly, delete the src/App.test.js file as we don’t need it for this tutorial.

1$ rm src/App.test.js

Next, let’s create a test file for the Books component.

1$ touch src/components/Books.test.js

Next, install the react-test-renderer library that enables you to render React components to pure JavaScript objects without depending on the DOM or a native mobile environment.

1$ npm install react-test-renderer@16.13.1

For instance, a <Link> component might look like this:

1{
2 type: 'a',
3 props: { href: 'https://www.facebook.com/' },
4 children: [ 'Facebook' ]
5}

Done? Let’s get testing!

How to write a snapshot test?

Remember your Books component that accepts a books array via the props?

1<Books books={books} />

Let’s create a snapshot for the three possible logical flows for our component: 1. Render a message when there are no books in a <span> tag 2. Render a single book in a <p> tag 3. Render multiple books in a <ol> tag

First, let’s snapshot the Books component when we pass no books. We use the react-test-renderer to snapshot the component’s output. Add the code below to your src/components/Books.test.js file.

1import React from 'react';
2import renderer from 'react-test-renderer';
3
4import Books from './Books';
5
6it('should render an empty message when no books', () => {
7 const elem = renderer.create(<Books />).toJSON();
8 expect(elem).toMatchSnapshot();
9});

Next, try to write test cases for scenarios 2 and 3.

Ready?

Here’s the solution.

1import React from 'react';
2import renderer from 'react-test-renderer';
3
4import Books from './Books';
5
6it('should render an empty message when no books', () => {
7 const elem = renderer.create(<Books />).toJSON();
8 expect(elem).toMatchSnapshot();
9});
10
11it('should render a single book', () => {
12 const books = ['Harry Potter']
13 const elem = renderer.create(<Books books={books} />).toJSON();
14 expect(elem).toMatchSnapshot();
15});
16
17it('should render multiple books', () => {
18 const books = ['Harry Potter', 'The Lord of the Rings']
19 const elem = renderer.create(<Books books={books} />).toJSON();
20 expect(elem).toMatchSnapshot();
21});

Alright, let’s run our tests via the terminal.

1$ npm test

If everything works well, all three tests should pass.

Successful tests

Note the src/components/__snapshots__ folder that the npm test command has created. This folder contains the snapshot outputs. Let’s take a look:

1// Jest Snapshot v1, https://goo.gl/fbAQLP
2
3exports[`should render a single book 1`] = `
4<p>
5 Harry Potter
6</p>
7`;
8
9exports[`should render an empty message when no books 1`] = `
10<span>
11 We didn't find any books.
12</span>
13`;
14
15exports[`should render multiple books 1`] = `
16<ol>
17 <li>
18 Harry Potter
19 </li>
20 <li>
21 The Lord of the Rings
22 </li>
23</ol>
24`;

It contains the rendered output for each of your test cases. But what happens when we update the UI? Let’s find out!

How to update snapshot tests?

So, you’ve rendered snapshot outputs to detect UI changes. Yet, you update the Books component, and now all tests fail. This section explains how you can update your snapshot tests.

First, change the <span> tag to a <b> tag when we pass an empty books array in src/components/Books.js.

1// No books on the list, render an empty message.
2return <b>We didn't find any books.</b>;

If you run npm tests in your terminal, the test case should render an empty message when no books fails.

Failed test run

Now, Jest comes into play. Actually, npm test uses the Jest CLI to run your tests. You will likely see the interactive mode. Press u to update the failing tests.

Jest interactive mode

However, you can also install the Jest CLI globally with npm install jest -g. This allows you to use the jest --updateSnapshot command from the terminal to update all snapshots. Personally, I prefer using Jest’s interactive mode.

Further, take a look at the src/components/__snapshots__/ folder. The Books.test.js.snap file contains the updated snapshot.

1exports[`should render an empty message when no books 1`] = `
2<b>
3 We didn't find any books.
4</b>
5`;

Also, you should see passing tests again. That’s simple, right?

Conclusion

This tutorial has shown you how to write snapshot tests and update them when your UI changes.

Jest snapshot testing is a great tool for React developers to detect unexpected UI changes. They are easy to create and maintain. Yet, make sure to write test cases for all possible flows in your React component. Remember that you can’t replace unit or functional tests with snapshot tests. Snapshot tests act as a simple tool to improve your code’s quality. In the end, you need unit and functional tests to verify your application’s behavior.

If you want to learn more about Jest snapshot testing, take a look at the best practices documentation by Jest.

Frontend Application Monitoring

A broken experience is not always due to an error or a crash, but may be the consequence of a bug or a slowdown that went unnoticed. Did my last release introduce that? Is it coming from the backend or the frontend? Asayer helps answer those questions and figure out what part of your code requires fixing or optimization. Because inconsistent performance simply drives customers away, causing retention and revenues to drop.

As we embrace agility, we push code more frequently than ever, and despite our best testing efforts, our code may end up breaking for a variety of reasons. Besides, frontend is different. It runs on different browsers, relies on complex JS frameworks, involves multiple CDN layers, gets affected by 3rd-party APIs, faulty internet connections, not-so-powerful devices and slow backends. In fact, frontend requires better visibility, and Asayer provides just that.

Your frontend is your business. It’s what people touch and feel. Let’s make it fast, reliable and delightful!

Start monitoring your web app for free.

More articles from Asayer Blog

A guide to the React useState hook

React hooks have been around for quite a while now and have changed the way developers go about building React components.

January 25th, 2021 · 14 min read

A Guide to Using localStorage in JavaScript Apps

The localStorage object is one of the most used objects in web programming. It provides a solution for storing key-value pairs locally.

January 20th, 2021 · 4 min read
© 2020 Asayer Blog
Link to $https://twitter.com/asayerioLink to $https://github.com/asayerioLink to $https://www.linkedin.com/company/18257552