Snapshot Testing
Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.
A typical snapshot test case renders a UI component, takes a snapshot, then compares it to a reference snapshot file stored alongside the test. The test will fail if the two snapshots do not match: either the change is unexpected, or the reference snapshot needs to be updated to the new version of the UI component.
Snapshot Testing with Jest
A similar approach can be taken when it comes to testing your React components. Instead of rendering the graphical UI, which would require building the entire app, you can use a test renderer to quickly generate a serializable value for your React tree. Consider this example test for a Link component:
import renderer from 'react-test-renderer';
import Link from '../Link';
it('renders correctly', () => {
const tree = renderer
.create(<Link page="http://www.facebook.com">Facebook</Link>)
.toJSON();
expect(tree).toMatchSnapshot();
});
The first time this test is run, Jest creates a snapshot file that looks like this:
exports[`renders correctly 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;
The snapshot artifact should be committed alongside code changes, and reviewed as part of your code review process. Jest uses pretty-format to make snapshots human-readable during code review. On subsequent test runs, Jest will compare the rendered output with the previous snapshot. If they match, the test will pass. If they don't match, either the test runner found a bug in your code (in the <Link>
component in this case) that should be fixed, or the implementation has changed and the snapshot needs to be updated.
The snapshot is directly scoped to the data you render – in our example the <Link>
component with page
prop passed to it. This implies that even if any other file has missing props (say, App.js
) in the <Link>
component, it will still pass the test as the test doesn't know the usage of <Link>
component and it's scoped only to the Link.js
. Also, rendering the same component with different props in other snapshot tests will not affect the first one, as the tests don't know about each other.
More information on how snapshot testing works and why we built it can be found on the release blog post. We recommend reading this blog post to get a good sense of when you should use snapshot testing. We also recommend watching this egghead video on Snapshot Testing with Jest.
Updating Snapshots
It's straightforward to spot when a snapshot test fails after a bug has been introduced. When that happens, go ahead and fix the issue and make sure your snapshot tests are passing again. Now, let's talk about the case when a snapshot test is failing due to an intentional implementation change.
One such situation can arise if we intentionally change the address the Link component in our example is pointing to.
// Updated test case with a Link to a different address
it('renders correctly', () => {
const tree = renderer
.create(<Link page="http://www.instagram.com">Instagram</Link>)
.toJSON();
expect(tree).toMatchSnapshot();
});
In that case, Jest will print this output:
Since we just updated our component to point to a different address, it's reasonable to expect changes in the snapshot for this component. Our snapshot test case is failing because the snapshot for our updated component no longer matches the snapshot artifact for this test case.
To resolve this, we will need to update our snapshot artifacts. You can run Jest with a flag that will tell it to re-generate snapshots:
jest --updateSnapshot
Go ahead and accept the changes by running the above command. You may also use the equivalent single-character -u
flag to re-generate snapshots if you prefer. This will re-generate snapshot artifacts for all failing snapshot tests. If we had any additional failing snapshot tests due to an unintentional bug, we would need to fix the bug before re-generating snapshots to avoid recording snapshots of the buggy behavior.
If you'd like to limit which snapshot test cases get re-generated, you can pass an additional --testNamePattern
flag to re-record snapshots only for those tests that match the pattern.
You can try out this functionality by cloning the snapshot example, modifying the Link
component, and running Jest.