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.
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.
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
:
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:
<ul>
).<span>
element.Finally, update App.js
to render our component:
- nano src/App.js
Replace the contents of App.js
with the following:
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.
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:
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"
:
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:
// 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:
// ...
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.
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:
...
/**
* 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:
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:
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:
// ...
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:
// ...
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:
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.
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.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!