In a Jest or Vitest component test, expect(container).toMatchSnapshot() passes the very first time it runs. Why does it always pass on that first run, and what risk does that create?
answer
- nothing to compare against yet
- records first, checks later
- sameness, not correctness
- the .snap is source code too
- CI mode refuses to write new ones
basics
~20 sOn the first run there is nothing to compare against, so the runner serializes the current output, writes it to a snapshot file, and passes. The baseline is whatever the code produced — correct or not — so an unreviewed snapshot can freeze a bug in place.
solid answer
~40 sA snapshot assertion compares serialized output against a stored baseline. The first time it runs there is no baseline, so the runner records the current output (into a `__snapshots__/*.snap` file, or inline in the test) and the assertion passes by definition. That means the test never verified that the output was *right* — only that it now exists. From then on it fails when the output changes, which is a change detector, not a correctness check. The practical consequences: read a new snapshot in code review the same way you read the code, keep it small enough to actually read, and run CI in a mode where a missing snapshot fails instead of being written silently, so nobody's machine quietly commits a fresh baseline for a component nobody checked.
go deeper
Know that a snapshot stores the output the first time and compares against it afterwards, and be able to say that the first run passes because there is nothing to compare with.
Explain that the baseline is a recording rather than a specification, and describe the safeguards: small captured values, reviewing the snapshot diff, and failing the build when a snapshot is missing rather than writing one.
Show that you treat snapshot files as reviewable artefacts and that you can spot the failure mode where a bug is baselined and the eventual fix shows up as a red test. Talk about what you require in review before a new baseline lands.
Own the policy angle: decide which categories of value are worth recording at all, make missing-snapshot-fails the default in CI, and be ready to defend why a suite full of recordings is weaker evidence than a smaller set of intent-carrying assertions.
## What a snapshot assertion actually is A snapshot assertion serializes some value — most often a rendered DOM subtree, sometimes a plain object — into text, and compares that text against a previously stored copy. The stored copy is called the snapshot, or baseline. In Jest and Vitest, `expect(value).toMatchSnapshot()` writes to a companion file under `__snapshots__/`, and `expect(value).toMatchInlineSnapshot()` writes the expected text back into the test file itself. The key point is what the runner is comparing. It has no model of what your component *should* render. It only knows what it rendered last time. ## Why the first run passes The first run finds no baseline. Rather than failing, the runner takes the current serialized output, stores it, and reports the assertion as passing. This is deliberate: it is what makes snapshots cheap to add. You write one line, run the suite, and a baseline appears. ```javascript // Run 1: no stored snapshot -> record output, pass // Run 2..n: serialize again, diff against the stored text, fail on any difference expect(container).toMatchSnapshot(); ``` So the very first execution is a *write*, not a *check*. Every execution after it is a check — but a check against a value that a machine chose, not one a human specified. ## The risk this creates If the component was broken when the snapshot was recorded, the snapshot enshrines the broken output. The test is now green forever, and worse, it will go **red** when someone fixes the bug. Reviewers who skim past a `.snap` file — it is large, auto-generated, and looks like noise — approve the bug into the baseline. Compare that with an explicit assertion: ```javascript expect(screen.getByRole('heading', { name: 'Invoice #1042' })).toBeInTheDocument(); ``` Here a human wrote down what "correct" means. If the component renders `Invoice #undefined`, this fails immediately, on the first run, before anything is committed. That is the difference between a specification and a recording. ## Working with the first-run behaviour instead of against it Four habits make snapshots safe to use: 1. **Review the snapshot as source.** A new or changed `.snap` hunk is a claim about output. If you cannot tell from the diff whether it is right, the snapshot is too big. 2. **Keep the captured value small.** Snapshotting an entire page produces hundreds of lines nobody reads; snapshotting one small subtree — or a plain data object derived from the UI — produces a diff a reviewer can judge. 3. **Fail on missing snapshots in CI.** Most runners support this: Jest's `--ci` flag makes a missing snapshot fail the run instead of writing one, and Vitest refuses to create new snapshots when it detects a CI environment. Without it, a snapshot file that was never committed is silently regenerated on the build machine and every test passes. 4. **Pair it with at least one explicit assertion.** Even one `getByRole` check anchors the test to intent, so a reviewer reading the test knows what it is supposed to prove. ## When a recording is the right tool anyway The first-run write is not always a defect. If the value is something you would never hand-write — a formatted string, a generated stylesheet, a serialized error message catalogue, a normalized data structure — recording it and then guarding it against unintended change is genuinely useful. The rule of thumb: a snapshot is appropriate when *any* change to the value deserves human attention, and inappropriate when the value churns for reasons unrelated to the behaviour under test. ## What to say in an interview Say plainly that snapshots assert *sameness*, not *correctness*; that the first run records rather than checks; and that the safeguard is review discipline plus a small captured value, not the tool. Candidates who claim a green snapshot proves the component works have missed the whole mechanism.
- If the first run only records, what makes the second and later runs fail?Later runs serialize the value again and diff the text against the stored baseline. Any difference — a changed class name, a reordered attribute, an added wrapper element — fails the assertion, whether or not the behaviour changed. That is why snapshots are called change detectors: they report difference, and a human still has to decide whether the difference was intended.
- A teammate deletes a snapshot file to fix a failing test and the suite goes green. What just happened?Deleting the baseline puts the test back into first-run mode, so the next run records the current output and passes. The failure was not fixed, it was overwritten. The correct move is to read the diff, decide whether the new output is right, and only then update the baseline deliberately — with the diff visible in review.
- Does a snapshot test guarantee the component rendered at all?It guarantees the assertion ran and produced serialized output matching the baseline. If the component rendered nothing, the baseline can legitimately be an empty container, and the test will pass forever. That is why an explicit assertion — that a specific role or text is present — belongs alongside or instead of the snapshot.
saying these in an interview costs you the question
- Says a passing snapshot proves the rendered output is correct
- Thinks the runner knows what the component is supposed to render
- Deletes or regenerates snapshot files to make a red test pass
- Never opens the .snap file during code review
- Believes the first run compares output against the component source