Luis Vieira

@luisvieira_gmr

Snapshot Testing With Jest — Beyond React components

Snapshots are a great new way of testing UI components but their applicability can go beyond that, and they can be really useful in other contexts too.

In reality with Jest snapshots it’s possible to test and assert the output of any serializable value.

Let’s assume the following scenario: we have a list of todos (Array<Obj>) and somewhere in our app we want to be able to filter them by one of it’s attributes. Let’s write a function and some tests for it:

var ToDos = [
{ 'Id': '1', 'Title': 'Shop groceries', 'Status': 'done' },
{ 'Id': '2', 'Title': 'Go to gym', 'Status': 'pending' },
{ 'Id': '3', 'Title': 'Kill it with fire', 'Status': 'done' },
{ 'Id': '4', 'Title': 'Take snapshots', 'Status': 'done' },
{ 'Id': '5', 'Title': 'Be awesome', 'Status': 'pending' },
];
function filterTodos(arr, key, term) {
return arr.filter(function(obj) {
return obj[key] === term
});
}
test('it returns all todos with matching Id',() =>{
expect(filterTodos(ToDos,'Id', '1')).toBe(
{ 'Id': '1', 'Title': 'Shop groceries', 'Status': 'done' }
)
})
test('it returns all todos with matching Title',() =>{
expect(filterTodos(ToDos,'Title', 'Shop groceries')).toBe(
{ 'Id': '1', 'Title': 'Shop groceries', 'Status': 'done' }
)
})
test('it returns all todos with matching Status',() =>{
expect(filterTodos(ToDos,'Done', 1)).toBe(
{ 'Id': '1', 'Title': 'Shop groceries', 'Status': 'done' },
{ 'Id': '3', 'Title': 'Kill it with fire', 'Status': 'done' },
{ 'Id': '4', 'Title': 'Take snapshots', 'Status': 'done' }
)
})

Normally when testing a functionality like this, we need a mocked list of todos and then we need to manually assert the expected return value.

Everything is working great but, if in the future we need to change our data we need to manually update each test.

Let’s say that we’re given a new requirement stating that all todos should have an expiration key with an expiration date. 
We would then have to manually update our array and the return value of each test case. This can be cumbersome specially in a large app with more complex features.

With snapshots we could replicate the same testing scenario as such:

var ToDos = [
{ 'Id': '1', 'Title': 'Shop groceries', 'Status': 'done' },
{ 'Id': '2', 'Title': 'Go to gym', 'Status': 'pending' },
{ 'Id': '3', 'Title': 'Kill it with fire', 'Status': 'done' },
{ 'Id': '4', 'Title': 'Take snapshots', 'Status': 'done' },
{ 'Id': '5', 'Title': 'Be awesome', 'Status': 'pending' },
];
function filterTodos(arr, key, term) {
return arr.filter(function(obj) {
return obj[key] === term
});
}
test('it returns all todos with matching Id',() =>{
expect(filterTodos(ToDos, 'Id', '1')).toMatchSnapshot()
})
test('it returns all todos with matching Title',() =>{
expect(filterTodos(ToDos, 'Title', 'Shop groceries')).toMatchSnapshot()
})
test('it returns all todos with matching Status',() =>{
expect(filterTodos(ToDos, 'Status', 'done')).toMatchSnapshot()
})

This should give you the following snapshot:

// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`it returns all todos with matching Id 1`] = `
Array [
Object {
"Id": "1",
"Status": "done",
"Title": "Shop groceries",
},
]
`;
exports[`it returns all todos with matching Status 1`] = `
Array [
Object {
"Id": "1",
"Status": "done",
"Title": "Shop groceries",
},
Object {
"Id": "3",
"Status": "done",
"Title": "Kill it with fire",
},
Object {
"Id": "4",
"Status": "done",
"Title": "Take snapshots",
},
]
`;
exports[`it returns all todos with matching Title 1`] = `
Array [
Object {
"Id": "1",
"Status": "done",
"Title": "Shop groceries",
},
]
`;

Then an update to the todos array would cause the tests to fail and produce the following output:

If this new output is expected we can replicate this through all the test cases simply by running the ```$ jest — updateSnapshots command, there’s no need to go through each test and update it’s return value.

This is why snapshots are really useful for apps with complex and frequently changing requirements and a really great tool for testing, it’s applicability can go far beyond react components.

Resources:

Topics of interest

More Related Stories