What is snapshot testing good and bad at? How do snapshots 'rot', and how do you use them responsibly?
Whether you understand snapshots as change-detectors, not correctness checks, and can avoid the classic failure mode.
A snapshot test renders something (a component, a serializable object) and saves its serialized output to a file on first run; subsequent runs DIFF the current output against the stored snapshot and fail if they differ. Its genuine value is CHANGE DETECTION with almost no assertion-writing: it alerts you when output changed unexpectedly, which is handy for catching accidental UI/markup regressions and for large structured outputs where hand-writing assertions is tedious. The critical mental model: a snapshot proves output DIDN'T CHANGE, not that it's CORRECT. The first snapshot is captured blind — if the component was already buggy, the snapshot enshrines the bug and happily 'passes'. That's the core weakness. The classic failure mode is snapshot ROT: large, whole-component snapshots change on almost every legitimate edit, producing constant diffs; developers stop reading them and reflexively run `jest -u` to update, at which point the snapshot tests nothing — they rubber-stamp whatever the code produces. Big snapshots also make diffs unreadable (hundreds of lines), so real regressions hide among noise. Responsible use: (1) keep snapshots SMALL and FOCUSED — snapshot a specific piece of output or use inline snapshots (toMatchInlineSnapshot) that live next to the test and get reviewed in the diff, not giant DOM dumps; (2) REVIEW every snapshot change in code review as deliberately as any assertion — an updated snapshot is a claim that the new output is correct; (3) prefer EXPLICIT assertions (getByRole, toHaveTextContent) for the behavior you actually care about, and use snapshots only as a supplementary net; (4) don't snapshot things that legitimately vary (dates, ids, random) without serializers/masks, or they'll be perpetually flaky; (5) treat a failing snapshot as a question ('did I mean to change this?'), and only update after confirming the new output is intended. The honest summary: snapshots are a cheap tripwire, not a substitute for asserting correctness — their value collapses the moment updating them becomes reflexive.
Deciding whether to snapshot; reviewing a PR full of updated snapshots; taming snapshot rot.
// Rot risk: a giant blind snapshot of a whole component
expect(render(<Dashboard />).container).toMatchSnapshot(); // 300-line diff, rubber-stamped
// Better: small, reviewable, inline
expect(formatMoney(1999)).toMatchInlineSnapshot('"$19.99"');
// Best for behavior you care about: an explicit assertion
expect(screen.getByRole('status')).toHaveTextContent('Saved');
// Rules: keep snapshots tiny, REVIEW every update, don't reflexively run 'jest -u'.