What Is Snapshot Testing
Snapshot testing captures a serialized representation of a value, such as a React component tree, a large configuration object, or an API response, and saves it to a file the first time the test runs. On every subsequent run, Jest re-serializes the current value and compares it character-for-character against the stored snapshot; if they differ, the test fails and shows a diff. This trades writing individual property assertions for a single broad comparison, which is efficient for catching unintended changes but says nothing about whether the originally captured output was actually correct.
Cricket analogy: Snapshot testing is like photographing the exact field placement at the start of an over so you can instantly spot if a fielder moves without permission mid-over, though the photo itself doesn't confirm the original placement was tactically sound.
Creating and Updating Snapshots
Calling expect(value).toMatchSnapshot() inside a test creates a new snapshot file the first time it runs, typically stored in a __snapshots__ directory next to the test file with a .snap extension, and Jest auto-generates the file if it doesn't exist yet. When the underlying code changes intentionally, the snapshot comparison will fail on the next run; running Jest with the -u (or --updateSnapshot) flag regenerates the stored snapshots to match the new output, which should be a deliberate, reviewed action rather than a reflex response to any failing snapshot test.
Cricket analogy: Updating a snapshot is like a groundskeeper deliberately re-marking the official pitch dimensions after an approved renovation, a formal, considered action, not something done reflexively every time a measurement looks slightly different from the file.
function formatInvoice(invoice) {
return {
id: invoice.id,
total: `$${invoice.total.toFixed(2)}`,
lineItems: invoice.items.map(i => `${i.qty}x ${i.name}`),
};
}
test('formats an invoice consistently', () => {
const invoice = { id: 'INV-1', total: 42.5, items: [{ qty: 2, name: 'Widget' }] };
expect(formatInvoice(invoice)).toMatchSnapshot();
});
// Run `jest -u` to intentionally regenerate this snapshot after a
// reviewed, approved change to formatInvoice's output.Never run jest -u reflexively just to make a red test suite pass. Read the diff first: if it reveals an unintended change, that's the snapshot test doing its job by catching a real regression, and the underlying code (not the snapshot) needs the fix.
Inline Snapshots
toMatchInlineSnapshot() works the same way as toMatchSnapshot() but writes the serialized value directly into the test file as a string literal argument, right next to the assertion, instead of a separate .snap file. This keeps small, focused snapshots visible in code review without needing to open a second file, which is especially useful for short outputs like a formatted string or a small object, though it becomes unwieldy for large component trees where a separate snapshot file keeps the test file readable.
Cricket analogy: Inline snapshots are like writing a fielding change directly into the scorer's running commentary at the moment it happens, rather than filing it in a separate log book you'd need to cross-reference later.
test('formats a short greeting inline', () => {
const greeting = formatGreeting('Ada');
expect(greeting).toMatchInlineSnapshot(`"Hello, Ada!"`);
});When to Use (and Avoid) Snapshots
Snapshots work best for output that is stable, deterministic, and meaningfully reviewed by a human at the moment it's created, such as a rendered component's DOM structure or a formatted report string; the reviewer approving the initial snapshot (and every subsequent -u update) is what gives the test its value. Snapshots work poorly for large, frequently-changing objects, output containing non-deterministic values like timestamps or random IDs (which should be normalized or mocked before snapshotting), and situations where developers are likely to run -u reflexively without reading the diff, since an unreviewed snapshot update simply enshrines whatever the code currently does, bugs included.
Cricket analogy: Good snapshot use is like keeping an official, human-verified pitch report before a Test match, reviewed by the curator; bad use is like blindly re-certifying the pitch report every single day without anyone actually walking the pitch to check it.
For non-deterministic values like Date.now() or generated UUIDs, use property matchers such as expect.any(String) inside toMatchSnapshot({ createdAt: expect.any(String) }), or mock the underlying source, so the snapshot doesn't fail on every run purely due to a changing timestamp.
- toMatchSnapshot() serializes a value and compares it against a stored .snap file on every run.
- The first run creates the snapshot; later runs fail if the serialized output differs.
- Running jest -u regenerates snapshots and should follow a deliberate review of the diff.
- toMatchInlineSnapshot() stores the snapshot directly in the test file for small, focused outputs.
- Snapshots are only as trustworthy as the human review behind the initial capture and every update.
- Avoid snapshotting non-deterministic data like timestamps or random IDs without normalizing them first.
- Large, frequently-changing objects are a poor fit for snapshot testing and often lead to reflexive -u updates.
Practice what you learned
1. What happens the first time expect(value).toMatchSnapshot() runs for a given test?
2. Why is it risky to run jest -u reflexively whenever a snapshot test fails?
3. Which is a poor candidate for snapshot testing without additional handling?
4. What is the main difference between toMatchSnapshot() and toMatchInlineSnapshot()?
5. What gives a snapshot test its actual value as a regression check?
Was this page helpful?
You May Also Like
Common Matchers: toBe, toEqual, toContain
Learn how Jest's core matchers toBe, toEqual, and toContain compare values, objects, and collections, and when to reach for each one.
Testing Async Code: Promises and async/await
Learn the correct patterns for testing promise-based and async/await code in Jest, and the pitfalls that let async bugs slip past a green test run.
Testing Exceptions with toThrow
Learn how to assert that a function throws an error using toThrow, including matching messages, error types, and async rejections.