DEV Community

loading...
Cover image for Snapshot Driven Development with Jest

Snapshot Driven Development with Jest

qmenoret profile image Quentin Ménoret Updated on ・2 min read

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'
}
Enter fullscreen mode Exit fullscreen mode

You’ll probably end up with a function like this:

function formatUser(user) {
  // transform and return the user
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Next, setup your test:

it("returns the user formatted", () => {
  const user = {
    firstName: "Marie",
    lastName: "Curie",
    birthdate: "07-11-1867",
  };
  expect(formatUser(user)).toMatchInlineSnapshot();
});
Enter fullscreen mode Exit fullscreen mode

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",
    }
  `);
});
Enter fullscreen mode Exit fullscreen mode

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"
    }
  `);
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (4)

pic
Editor guide
Collapse
philou profile image
Philippe Bourgau • Edited

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.

expect(fromAPIFormat(toAPIFormat(input))).toEqual(input))
Enter fullscreen mode Exit fullscreen mode

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!

Collapse
qmenoret profile image
Quentin Ménoret Author

Very good point thanks for sharing!

Collapse
elthrasher profile image
Matt Morgan

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...

Collapse
ben profile image
Ben Halpern

Yeah, I think it's one of those tools which needs to be used well.