Disclaimer: this is not something you can or should apply all the time. Only some very specific situations make it relevant. It makes no sense to use this on simple functions, with simple return values.
Imagine you are working with an external API, and have to transform the internal format to the one the API accepts. Like this:
const user = {
firstName: 'Marie',
lastName: 'Curie',
birthdate: '07-11-1867'
}
const output = {
first_name: 'Marie',
last_name: 'Curie',
date_of_birth: '1867.11.07'
}
You’ll probably end up with a function like this:
function formatUser(user) {
// transform and return the user
}
Most of the time, when I have to write such a function, I would apply TDD. But if there are a lot of fields to cover this can be quite tedious. Which is why I often use snapshots to actually write down the result first, then iterate until the test is green. Then I might split up my tests. Let's see how to do this.
First, make your function just return the user as is:
function formatUser(user) {
return user;
}
Next, setup your test:
it("returns the user formatted", () => {
const user = {
firstName: "Marie",
lastName: "Curie",
birthdate: "07-11-1867",
};
expect(formatUser(user)).toMatchInlineSnapshot();
});
Now run the test, Jest will change the code to this:
it("returns the user formatted", () => {
const user = {
firstName: "Marie",
lastName: "Curie",
birthdate: "07-11-1867",
};
expect(formatUser(user)).toMatchInlineSnapshot(`
Object {
"birthdate": "07-11-1867",
"firstName": "Marie",
"lastName": "Curie",
}
`);
});
You can now just edit the inline snapshot to match what you actually want to return:
expect(formatUser(user)).toMatchInlineSnapshot(`
Object {
"date_of_birth": "1867.11.07",
"first_name": "marie",
"last_name": "curie"
}
`);
Then iterate on your code until the test is green.
Of course here you have only 3 properties on your object so it's rather easy to do it manually. You could just have used a .toEqual
and write down the object yourself. But when the output is complex (imagine tens of properties), or changes several time during development, I find snapshots easier to work with.
Another very nice application for this is when you want your code to return an object, but you're not quite sure yet what it will look like. You can just keep running your test, editing the snapshot as you go (in jest watch mode, that just means hitting u
).
If the snapshot becomes too big, but you want to keep it anyway, I would suggest to just remove the string, and change the call to toMatchSnapshot
. This way the snapshot will be stored in a different file.
Top comments (4)
Thanks for showing a good use of Jest Snapshots. Indeed, they can be misused, but they can also come in pretty handy at time. I've also used snapshot effectively when testing legacy code.
Here is another trick I have used successfully in the situation you describe. Very often when you have a format conversion function, you also have the inverse function (it would be the one converting from API to your internal representation). In this case, it can be handy to test the 2 together.
This way you can test many variations of data without having to resort to snapshot every time. Basically, you can skip snapshots when testing the fromAPIFormat function.
Thanks for your post!
Very good point thanks for sharing!
Nice writeup. I think jest snapshots got a bit of a bad rap because people would snapshot giant enzyme'd component graphs for react and they'd mutate constantly, but using snapshots for APIs and things that are more stable is really helpful in my experience.
Also you can use matchers on parts of your payload that might mutate and just match any string/object/etc. jestjs.io/docs/en/snapshot-testing...
Yeah, I think it's one of those tools which needs to be used well.