mediumEngineering Practices#75

Snapshot testing

Prompt

Write a snapshot test for a simple Greeting component that accepts a name prop. The snapshot should capture the rendered output.

Solution

it('matches snapshot', () => {
  const tree = render(<Greeting name="Alice" />)
  expect(tree).toMatchSnapshot()
})
Mentor's take

Snapshot tests are a regression tripwire, not a specification. toMatchSnapshot serializes the rendered tree to a .snap file on first run, commits it, and fails any future run whose output differs. The value is coverage-per-keystroke: one line guards the entire rendered structure of a presentational component against accidental change.

The trap is that a snapshot asserts "output equals whatever it was" — it encodes the current state as correct without anyone deciding it is. That has two failure modes. First, giant snapshots of deep trees fail on every unrelated change, and a test that cries wolf gets -u'd without reading. Second, jest -u run blindly converts a real regression into a committed expectation — the tool's one-keystroke update is its own biggest hazard. The discipline: keep snapshots small (single presentational components, or inline snapshots that live in the test file where reviewers actually see them), and treat a snapshot diff in review with the same attention as a code diff, because it is the assertion changing.

Behavioral assertions still beat snapshots for anything with logic — snapshot the static Greeting, but test the Counter by pressing and asserting.

Red flag: "we have great coverage, it's all snapshots" — serialization coverage isn't behavioral coverage, and habitual -u means the suite approves anything.

Say it: "Snapshots are tripwires for presentational components — I keep them small and review .snap diffs as assertions, because updating one blindly is deleting a test."