Snapshot testing React components with Jest

Since version 14 Jest introduced a feature called snapshot testing. Snapshot testing is a way to assert the result of a given test by generating a Json representation of its output.

Snapshot Testing how does it work?

The first time your test is run, a Json representation of your expected output is saved to a file. The next time you run the same test, a diff runs between a new and the previously stored snapshot. If there are no differences the test passes, otherwise the test fails and the resulting diff is returned.

Snapshot testing flow

How to use it

Given a simple React component:

import React from 'react';
const Button = ( props ) => (
<button className="button"
aria-pressed={ props.pressed}>
{ props.text }
</button>
);
export default Button;

We can test it as:

import React from 'react';
import renderer from 'react-test-renderer';
import Button from './Button';
test('<Button /> renders with text as prop', () => {
const tree = renderer.create(
<Button text="add to bag" pressed="false"/>
).toJSON();
expect(tree).toMatchSnapshot();
});

And we would get the following snapshot result saved to a .snap file:

exports[`test <Button /> renders correctly 1`] = `
<button
aria-pressed="false"
className="button">
add To bag
</button>
`;

Whenever the component output is changed a diff is returned to the console:

If the change is intended you can update your snapshot running `$ jest — updateSnapshot`, otherwise you’ll need to fix your component so that it’s output matches the stored snapshot.

Why is this any better?

With snapshots you’re testing the full render output of your component, in an easy and simple way. Otherwise you would have to manually select and verify the presence of each of the required component attributes.

Normally you would do this by selecting each element you want to test for by traversing the DOM tree of the component.

import React from 'react';
import { expect } from 'chai';
import { render } from 'enzyme';
import Button from './Button';
describe('<Button />', () => {
it('has a .button classname', () => {
const wrapper = render(<Button/>);
expect(wrapper.find('.button').length).to.equal(1);
});
  it('has [aria-pressed] attribute', () => {
const wrapper = render(<Button />);
expect(wrapper.find('[aria-pressed]').length).to.equal(1);
});

it('displays the text prop', () => {
const wrapper = render(<Button text="Add to Bag" />);
expect( wrapper.text() ).to.contain('Add to Bag');
});
});

This kind of testing is much harder to develop and maintain as UI components tend to change a lot, specially if you work on an app with complex and frequently changing requirements.

Keep in mind that this is a really simplified component, in a real world app you will be dealing with a lot more complexity both in requirements and component structure, so the complexity of your tests can easily increase exponentially.

A snapshot also ensures the order of the elements in your component is exactly the same. The tests we just made, only test for the presence of the elements. Although we could add some logic to verify if each element appears in the intended order, that would just add up to the complexity and maintenance of these tests.

Advantages / Disadvantages of snapshot testing

Advantages:

  • Easy to develop, which leads to more tests being written;
  • Easy to update, makes change easy;
  • Test the exact output of your component hasn’t changed;
  • A more declarative way of testing

Disadvantages:

  • You can’t TDD, the snapshot is taken after the component is developed
  • Snapshots don’t tell you what the output should be, they just ensure it hasn’t changed

Snapshot testing when used with React components dramatically simplifies the testing of UI components, making them less expensive to write and easier to maintain.