Snapshot Test a Virtual DOM for Regressions
Render a component to a virtual DOM, serialize the tree to a stable string, and diff the snapshot on every run so structural changes fail the test.
Task
Catch unintended structural changes to a component by snapshotting its rendered virtual DOM and failing on diff.
When to use this
A refactor renames a class and drops a wrapper div, but the visual output looks the same in manual testing. The team wants a test that fails when the structure changes, not just when the pixels change.
Prerequisites
- A test runner with snapshot support (vitest or jest)
- A render function that produces a virtual DOM tree
- A serializer that yields a stable, diffable string
Steps
1. Render the component with fixed props and serialize the tree to a stable string.
const tree = render(<Card title="Hello" />);
expect(serialize(tree)).toMatchInlineSnapshot();
Expected output: The snapshot is written next to the test as a stable, indented string that captures the full structure.
2. Run the test in watch mode and update the snapshot only when the change is intended.
vitest -uExpected output: A deliberate change updates the snapshot file; an accidental change fails the test with a diff.
3. Review the snapshot diff in the pull request before merging, so a structural change is never silent.
git diff -- "**/__snapshots__/*"Expected output: The PR shows the snapshot diff, and a reviewer can confirm the change is intended.
Troubleshooting
Problem
The snapshot includes volatile values like timestamps
Solution
Mock the clock or use a serializer that replaces volatile fields with placeholders before snapshotting.
Problem
Snapshots grow large and become unreadable
Solution
Snapshot small components in isolation, and prefer inline snapshots for short output so the assertion lives next to the test.
Limitations
- A snapshot test confirms structure, not behavior; a component can render the wrong structure and still match a stale snapshot.
- Updating a snapshot bypasses review unless the diff is checked; make the snapshot file a required reviewer.
Serialize, then diff
Render the component with fixed props and serialize the tree to a stable string. The snapshot is the source of truth; a structural change produces a diff that fails the test.
const tree = render(<Card title="Hello" />);
expect(serialize(tree)).toMatchInlineSnapshot();
Update with intent
Run vitest -u to update the snapshot only when the change is intended. The diff lands in the pull request, so a reviewer sees the structural change. An accidental rename or a dropped wrapper fails the test before it merges.
git diff -- "**/__snapshots__/*"
Expected outcome
A structural change fails the test with a readable diff, an intended change updates the snapshot and shows in the PR, and volatile values are masked so they do not produce false diffs. Snapshot small components in isolation so the files stay readable.