← Recipes

阅读中文版本

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.

~20 mintestingsnapshotdom

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 -u

Expected 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.