PushBackLog

Snapshot Testing

Advisory enforcement Complete by PushBackLog team
Topic: testing Topic: frontend Skillset: frontend Skillset: fullstack Technology: generic Stage: execution Stage: review

Snapshot Testing

Status: Complete
Category: Testing
Default enforcement: Advisory
Author: PushBackLog team


Tags

  • Topic: testing, frontend
  • Skillset: frontend, fullstack
  • Technology: generic
  • Stage: execution, review

Summary

Snapshot testing captures the rendered output or serialised state of a component or function, saves it as a reference file, and then compares subsequent outputs against that reference. Unexpected changes fail the test. It is most useful for detecting unintended regressions in UI components and serialised data structures with many fields. Used carefully, it protects against silent drift; used carelessly, it becomes a collection of assertions that engineers update without reviewing.


Rationale

Preventing unintended visual and structural regressions

Rendering a UI component produces an output with dozens or hundreds of HTML attributes, class names, event bindings, and text nodes. Writing assertions for each individual property is impractical. A snapshot test captures the entire rendered tree once and fails if anything changes unexpectedly — forcing an engineer to acknowledge the change before updating the reference.

The same principle applies to serialised data structures (API responses, event payloads, configuration outputs) with many fields: snapshot testing catches additions, removals, and changes that might otherwise go unnoticed.

The snapshot update trap

The principal risk with snapshot testing is that engineers update snapshots reflexively without reviewing whether the change is correct. When a test says “snapshot changed”, the correct response is to inspect the diff and verify that the change is intentional. The incorrect response is to run --updateSnapshot and commit without looking. This behaviour degrades snapshots from a safety net to a false confidence generator.


Guidance

UI component snapshots (Jest + React Testing Library)

// UserCard.test.tsx
import { render } from '@testing-library/react';

test('UserCard renders a premium user correctly', () => {
  const { container } = render(
    <UserCard
      user={{ name: 'Christy Arthur', plan: 'premium', avatarUrl: null }}
    />
  );
  expect(container).toMatchSnapshot();
});

First run: creates __snapshots__/UserCard.test.tsx.snap
Subsequent runs: compares against the saved snapshot

Reviewing snapshot diffs

# Update a specific snapshot after confirming the change is intentional
jest -u UserCard.test.tsx

# Never run blindly:
jest -u   # updates ALL snapshots without review

When a snapshot diff appears in a PR, the reviewer should inspect the diff file and confirm that every changed line represents intended behaviour.

Inline snapshots for smaller outputs

Inline snapshots live in the test file rather than a separate .snap file:

test('formats a price correctly', () => {
  expect(formatPrice(4999, 'GBP')).toMatchInlineSnapshot(`"£49.99"`);
});

Prefer inline snapshots for small, human-readable outputs. Use file snapshots for large rendered trees where the snapshot file is acceptable to maintain.

Serialised data snapshot testing

// Snapshot an API response shape to detect unintended changes
test('GET /orders/:id response shape', async () => {
  const response = await request(app).get('/orders/test-order-1');
  expect(response.body).toMatchSnapshot();
});

Use this for regression detection, not as a substitute for assertions on business-critical fields.

Dos and don’ts

DoDon’t
Snapshot stable components with complex outputSnapshot components whose output changes with every render (dates, random IDs)
Review snapshot diffs in code reviewUpdate snapshots without reading the diff
Use inline snapshots for small outputsCreate enormous snapshots that nobody reads
Store snapshot files in version controlIgnore snapshot files or gitignore them
Write focused assertions for business-critical propertiesRely solely on snapshots for correctness

Stabilising dynamic content

Snapshots that include dynamic values (dates, UUIDs, random values) fail on every run. Stabilise them before snapshotting:

// Mock dynamic values to make snapshots stable
jest.useFakeTimers().setSystemTime(new Date('2026-01-01'));

// Or replace dynamic values with deterministic substitutes
const user = {
  id: 'test-id-fixed',
  createdAt: '2026-01-01T00:00:00.000Z',
  name: 'Test User',
};

Best fit for snapshot testing

Snapshot testing is well-suited for:

  • UI components with complex, stable HTML output
  • API response shape regression detection
  • CLI tool output verification
  • Compiled/generated output (GraphQL schema, OpenAPI spec, generated types)

It is a poor fit for logic-heavy functions where explicit assertions are more expressive.