Snapshot testing stores a serialized copy of an output the first time a test runs, then fails the test the moment a later run produces anything different. That single mechanism makes it either the easiest safety net you will ever add or a check nobody actually verifies, depending entirely on what you snapshot and how big the diff gets. The tool is the same either way; the outcome depends on judgment the tool cannot supply for you.
How it works
import { expect, it } from "vitest";
import { formatInvoice } from "./formatInvoice";
it("formats an invoice consistently", () => {
const result = formatInvoice({ id: "inv_1", total: 4200 });
expect(result).toMatchSnapshot();
});
The first run writes the result to a snapshot file. Every later run recomputes formatInvoice and diffs the result against that stored file. A developer either fixes an unintended change, or runs the update flag to accept the new output as the new correct answer.
When it helps versus when it hurts
| Situation |
What happens |
Verdict |
| A small component's rendered markup |
Cheap to write, catches unplanned structural change |
Helps |
| A serialized API response as a compatibility check |
Any diff is a real signal worth a look |
Helps |
| An entire page render |
One unrelated change anywhere fails the test |
Hurts |
| Output containing a timestamp or random ID |
Fails every run unless normalized first |
Hurts |
| A 400-line generated file diff |
Reviewers stop reading and just click update |
Hurts |
The pattern is consistent: snapshots help when a diff, if one appears, is small enough that a human will actually read it. They hurt the moment the diff is too large or too noisy for that to realistically happen.
Keeping snapshots a real safety net
- Scope each snapshot to the smallest unit that matters. Snapshot the component or value under test, not the whole page or object graph around it.
- Normalize non-deterministic fields before comparing. Mask timestamps, UUIDs, and locale-formatted values so a snapshot fails for a real reason, not a coincidence of when it ran.
- Review every diff before accepting it. Treat the update flag as a decision, not a formality — an unread "yes" defeats the entire point of the test.
- Pair snapshots with explicit assertions on values that actually matter. Let the snapshot catch the unexpected; let a real assertion guarantee the specific thing you already know must be true.
Common mistakes
Snapshotting an entire page instead of the component under test. A single unrelated change anywhere on the page fails the test, and the resulting diff is too large to review carefully.
Approving snapshot updates without reading the diff. Running the update command is one keystroke; doing it without reading what changed turns a safety net into a rubber stamp.
Capturing non-deterministic values. Timestamps, random IDs, and locale-dependent formatting make snapshots flaky unless normalized before comparison.
Using a snapshot as the only test for logic-heavy code. A snapshot shows that output changed, not why, or whether the new output is even correct — pair it with assertions for anything with real business logic.
FAQ
How do I know if a snapshot has gotten too big to be useful?
If you cannot describe in one sentence what a diff in that snapshot would mean, it is too big. Split it into smaller, more specific snapshots that each test one thing.
Should snapshot failures block a release?
Yes, the same as any other test failure — the difference is what happens next should be investigation, not an automatic update, since a snapshot cannot tell you whether the new output is correct.
Do snapshot tests replace visual regression tests?
No. A text or markup snapshot cannot catch a CSS or layout regression the way a pixel-level visual diff tool can.
Why do snapshot tests sometimes pass locally but fail in CI?
Usually non-deterministic content — timestamps, machine-specific paths, or locale differences — that was not normalized before the snapshot was taken.
Where to go next
See mutation testing explained for 2026 for a way to check whether your assertions catch anything at all, the test pyramid explained for 2026 for where snapshot tests fit relative to unit and integration layers, and what an array is in 2026 for the kind of serialized structure snapshots often capture.