Snapshot testing in JavaScript

Snapshot testing in JavaScript

3 min read

Jest is an opinionated (as opposed to completely customizable) and powerful framework of tools for testing JavaScript. The developers behind Jest highlight three main feature on the official website:

  1. It's ready-to-use out of the box with "zero configuration"
  2. Provide really nice feedback when things break
  3. Introduces a new way of testing code called "snapshot testing"

Point 1 and 2 are quite intuitive, however, snapshots require a little more explanation. Rogelio Guzman has a great intro talk to snapshot testing that I definitely recommend! I will continue discussing the concepts here and where I see the biggest benefit of using snapshots.

When I write tests (especially UI tests), one of the hardest things is coming up with a set of assertions to validate a given function. In addition, my main concern is to prevent stuff from unexpectedly breaking as I introduce changes to the code base.

Snapshot testing to the rescue! With Jest set up, all you need to do is:

import { formatDate } from './utils'

test('formats correctly', () => {
  const someDate = new Date('2008-02-17')

What's going on here

The first time you run the test suite:

  1. The function runs and produces output
  2. The output is serialized and stored in a snapshot file

    This file should be committed to source control!

  3. Test passes

Subsequent test runs follow this pattern:

  1. The function runs and produces output
  2. The output is serialized
  3. The existing snapshot is retrieved and compared with the new snapshot
  4. If they match, test passes ✅. If they mismatch, test fails ❌.

I've tried to summarize the process in the figure below:

Snapshot testing diagram

Failed tests can be evaluated by the developer to see if the changes are expected or not. When changes are expected, you re-run the tests, telling Jest to update the snapshot. If changes are not expected, you have to manually work out what went wrong.

Use cases for snapshots

  • In general, snapshots work great for pure functions. In addition, they're specifically useful for functions that produce complex output where it's not clear which parts should be asserted.

Examples include functions that return large JSON objects and e.g. stateless React UI components.

  • With snapshots, you can also test many edge cases relatively cheaply and without repeating assertions.

  • ☝️ If you already use Storybook to setup UI component examples - you can get snapshot testing essentially for free with the Storyshots addon.

Limitations of snapshots

  • Functions that cause side effects (not pure) like updating a database normally don't generate output - therefore, there's nothing to "snapshot".

  • Not so much a limitation but it's good to remember that even with snapshots, you will still to mock e.g. third-party APIs and/or HTTP requests.

I hope this article sheds some light on the sometimes confusing and magical properties of snapshot testing! If you learned something new, please consider sharing this article with your friends and colleagues 💬