Snapshot testing
A snapshot test records a serialized output once and fails when it changes.
Used well, it pins a component's rendered markup or a response's shape
cheaply. Used badly, it becomes the worst kind of test: a giant blob nobody
reads, a diff approved with --update on reflex, a green check that asserts
only that today equals yesterday. The discipline is limiting what you
snapshot and treating every diff as a claim to verify.
Method
- Snapshot serialized output, not behavior. Snapshots fit stable
serializable artifacts: rendered HTML, a JSON response, a formatter's
output. For logic with a known expected value, write an explicit
assertion, since a snapshot there only hides the intent.
- Keep each snapshot small enough to read. Cap it at what a reviewer
will actually scan, roughly a screen. Snapshot one component, not a whole
page tree. A thousand-line snapshot gets rubber-stamped, which defeats the
test.
- Review every diff as a real change. A snapshot diff is the test asking
whether you meant to change this. Read it, confirm the change is intended,
and only then update. Update-all in a hook or CI turns the test into a
rubber stamp.
- Strip nondeterminism before serializing. Replace timestamps, UUIDs,
and random ids with property matchers like
expect.any(String) or stable
fixtures. A snapshot that changes every run trains people to ignore its
diffs.
- Store snapshots as reviewed source. Commit them, read them in the pull
request like code, and never let a snapshot land unseen. A snapshot nobody
looked at is a recorded output, not an assertion.
- Prefer inline snapshots for small values. Keep short snapshots inline
with the test using
toMatchInlineSnapshot so the expected output sits
next to the code, not in a distant .snap file the reader never opens.
Litmus tests
- Could a reviewer read this snapshot's diff and judge whether the change was
intended?
- Does the snapshot exclude every value that varies between runs?
- Is anything asserted here by snapshot that a three-line explicit assertion
would state more clearly?
Boundaries
Snapshots pin serialized output, they do not verify logic or catch behavior a
human never reviews: for value assertions use unit-test-design, and for large
legacy outputs use golden-master with its own diff workflow. Follow the
framework's snapshot format, Jest, Vitest, or insta, over the specifics here.
1---2name: snapshot-testing3description: Use snapshot tests for serialized output only, with disciplined review of every diff and hard limits on snapshot size. Use when pinning stable rendered output like markup or an API response shape.4---56# Snapshot testing78A snapshot test records a serialized output once and fails when it changes.9Used well, it pins a component's rendered markup or a response's shape10cheaply. Used badly, it becomes the worst kind of test: a giant blob nobody11reads, a diff approved with `--update` on reflex, a green check that asserts12only that today equals yesterday. The discipline is limiting what you13snapshot and treating every diff as a claim to verify.1415## Method16171. **Snapshot serialized output, not behavior.** Snapshots fit stable18 serializable artifacts: rendered HTML, a JSON response, a formatter's19 output. For logic with a known expected value, write an explicit20 assertion, since a snapshot there only hides the intent.212. **Keep each snapshot small enough to read.** Cap it at what a reviewer22 will actually scan, roughly a screen. Snapshot one component, not a whole23 page tree. A thousand-line snapshot gets rubber-stamped, which defeats the24 test.253. **Review every diff as a real change.** A snapshot diff is the test asking26 whether you meant to change this. Read it, confirm the change is intended,27 and only then update. Update-all in a hook or CI turns the test into a28 rubber stamp.294. **Strip nondeterminism before serializing.** Replace timestamps, UUIDs,30 and random ids with property matchers like `expect.any(String)` or stable31 fixtures. A snapshot that changes every run trains people to ignore its32 diffs.335. **Store snapshots as reviewed source.** Commit them, read them in the pull34 request like code, and never let a snapshot land unseen. A snapshot nobody35 looked at is a recorded output, not an assertion.366. **Prefer inline snapshots for small values.** Keep short snapshots inline37 with the test using `toMatchInlineSnapshot` so the expected output sits38 next to the code, not in a distant `.snap` file the reader never opens.3940## Litmus tests4142- Could a reviewer read this snapshot's diff and judge whether the change was43 intended?44- Does the snapshot exclude every value that varies between runs?45- Is anything asserted here by snapshot that a three-line explicit assertion46 would state more clearly?4748## Boundaries4950Snapshots pin serialized output, they do not verify logic or catch behavior a51human never reviews: for value assertions use unit-test-design, and for large52legacy outputs use golden-master with its own diff workflow. Follow the53framework's snapshot format, Jest, Vitest, or insta, over the specifics here.