Introduction
Since I heard about snapshot testing at the 2017 React Europe conference, I was curious about this new tool for my daily testing.
I must admit that I am a Mocha
lover but Jest
really caught my attention with this feature.
At some point during testing, everyone has to print the expected result of the test and copy it to your test file. Now, with Jest
snapshots, this is not required anymore. Jest will save the expected result for us. Yeah, I know, a dream come true!
A Simple Library Test
Step 1
The first step is to install Jest
itself.
// Using NPM
npm install -D jest-cli
// Using Yarn
yarn add jest-cli
Be sure to be using a version higher than 14 since that was when snapshots
were added to Jest. We can check the version using
➜ npm view jest-cli version
21.2.1
Step 2
I created a simple movie normalizer library.
const movieNormalizer = (movieData) => ({
title: movieData.Title,
description: movieData.Plot,
language: movieData.Language,
writers: movieData.Writers.split(','),
actors: movieData.Actors.split(','),
genres: movieData.Genre.split(','),
});
export default movieNormalizer;
The objective is simple. From this JSON object:
{
"Title": "Game of Thrones",
"Year": "2011–",
"Rated": "TV-MA",
"Released": "17 Apr 2011",
"Runtime": "57 min",
"Genre": "Adventure, Drama, Fantasy",
"Director": "N/A",
"Writer": "David Benioff, D.B. Weiss",
"Actors": "Peter Dinklage, Lena Headey, Emilia Clarke, Kit Harington",
"Plot": "Nine noble families fight for control over the mythical lands of Westeros, while a forgotten race returns after being dormant for thousands of years.",
"Language": "English",
"Country": "USA, UK",
"Awards": "Won 1 Golden Globe. Another 249 wins & 422 nominations.",
"Ratings": [
{
"Source": "Internet Movie Database",
"Value": "9.5/10"
}
],
}
we want to get to this normalized object:
{
"title": "Game of Thrones",
"description": "Nine noble families fight for control over the mythical lands of Westeros, while a forgotten race returns after being dormant for thousands of years.",
"language": "English",
"writers": ["David Benioff", " D.B. Weiss"],
"actors": ["Peter Dinklage", " Lena Headey", " Emilia Clarke", " Kit Harington"],
"genres": ["Adventure", " Drama", " Fantasy"]
}
Step 3
Let us add the Jest
runner to our NPM
script list.
"scripts": {
"test": "jest"
},
Now, it is time to create our test file.
import movieNormalizer from '../src/movieNormalizer';
import movie from './movie.json';
describe('Movie Normalizer', () => {
test('should return a normalized object from the JSON API schema', () => {
const normalizedMovie = movieNormalizer(movie);
expect(normalizedMovie).toMatchSnapshot();
});
});
The magic happens when we use the toMatchSnapshot()
function. This function creates a new snapshot file, if there were no prior snapshots, and will use this one to compare results with any future changes.
Run npm run test
.
PASS tests/movieNormalizer.test.js
Movie Normalizer
✓ should return a normalized object from the JSON API schema (6ms)
› 1 snapshot written.
Snapshot Summary
› 1 snapshot written in 1 test suite.
The 1 snapshot written.
tells us that now we have a new snapshot file. We can look in the __snapshots__
folder and view our new snapshot file.
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Movie Normalizer should return a normalized object from the JSON API schema 1`] = `
Object {
"actors": Array [
"Peter Dinklage",
" Lena Headey",
" Emilia Clarke",
" Kit Harington",
],
"description": "Nine noble families fight for control over the mythical lands of Westeros, while a forgotten race returns after being dormant for thousands of years.",
"genres": Array [
"Adventure",
" Drama",
" Fantasy",
],
"language": "English",
"title": "Game of Thrones",
"writers": Array [
"David Benioff",
" D.B. Weiss",
],
}
`;
Step 4
What happens when we need to update our snapshots?
I added this change to my normalizer library:
writers: movieData.Writer.split('/'),
I changed the ,
to /
to make the test fail.
FAIL tests/movieNormalizer.test.js
Movie Normalizer
✕ should return a normalized object from the JSON API schema (9ms)
● Movie Normalizer › should return a normalized object from the JSON API schema
expect(value).toMatchSnapshot()
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -12,9 +12,8 @@
" Fantasy",
],
"language": "English",
"title": "Game of Thrones",
"writers": Array [
- "David Benioff",
- " D.B. Weiss",
+ "David Benioff, D.B. Weiss",
],
}
We only need to add the --updateSnapshot
parameter and Jest
will update our current snapshot.
> jest "--updateSnapshot"
PASS tests/movieNormalizer.test.js
Movie Normalizer
✓ should return a normalized object from the JSON API schema (6ms)
› 1 snapshot updated.
Snapshot Summary
› 1 snapshot updated in 1 test suite.
Testing React Components
Step 1
For React, I created a new simple component.
import React from 'react';
const MovieComponent = ({ movie }) => (
<div> <h1>My Movie</h1> <ul> <li><b>Title:</b> {movie.title}</li> <li><b>Description:</b> {movie.description}</li> <li><b>Language:</b> {movie.language}</li> <li> <b>Writers:</b> {movie.writers.map((writer) => writer).join(',')} </li> <li> <b>Actors:</b> {movie.actors.map((actor) => actor).join(',')} </li> <li> <b>Genres:</b> {movie.genres.map((genre) => genre).join(',')} </li> </ul> </div>
);
export default MovieComponent;
Step 2
Now let us add our new react test file.
import React from 'react';
import renderer from 'react-test-renderer';
import movie from './movie.json';
import movieNormalizer from '../src/movieNormalizer';
import MovieComponent from '../src/MovieComponent';
describe('Movie Component', () => {
test('should render the movie information', () => {
const normalizedMovie = movieNormalizer(movie);
const component = renderer.create(
<MovieComponent movie={normalizedMovie} />
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
It will work exactly as the example before. With any changes we make, the test will fail until we update our new snapshot.
Conclusion
Snapshot testing is a really great feature of Jest
. I hope this feature finds its way to others runners like e.g. mocha
as well. Maybe we do not require snapshot in all of our scenarios or components, but I know I will start using it more every day.
Links
Up next
Lorem ipsum dolor sit amet, consectetur adipiscing elit.