Snapshot Testing

toMatchSnapshot — Basic Usage

import { render } from '@testing-library/react'; import { Button } from './Button'; test('Button renders correctly', () => { const { asFragment } = render(<Button label="Click me" />); expect(asFragment()).toMatchSnapshot(); }); // For plain objects / values test('transforms user data', () => { const result = transformUser({ id: 1, name: 'Alice', role: 'admin' }); expect(result).toMatchSnapshot(); }); // Snapshot file is saved at __snapshots__/Button.test.tsx.snap // Example .snap content: // exports[`Button renders correctly 1`] = ` // <DocumentFragment> // <button class="btn btn-primary"> // Click me // </button> // </DocumentFragment> // `;

Inline Snapshots

// Jest writes the snapshot value inline on first run test('formats currency', () => { expect(formatCurrency(1234.5, 'USD')).toMatchInlineSnapshot(`"$1,234.50"`); }); test('error object shape', () => { const err = createError('NOT_FOUND', 'User not found'); expect(err).toMatchInlineSnapshot(` Object { "code": "NOT_FOUND", "message": "User not found", "timestamp": Any<String>, } `); }); // Benefit: snapshot lives with the test — easier to review in PRs // Use expect.any() for dynamic values expect({ id: expect.any(Number), createdAt: expect.any(String), name: 'Alice', }).toMatchInlineSnapshot();

Updating Snapshots

# Update all obsolete/changed snapshots npx jest --updateSnapshot npx jest -u # Update snapshots for a specific test file npx jest Button.test.tsx --updateSnapshot # Interactive snapshot update mode npx jest --watch # then press 'u' to update failing snapshots # Delete obsolete snapshots (no longer referenced) npx jest --ci # fails on new/changed snapshots in CI # jest.config.js — warn on obsolete snapshots module.exports = { snapshotFormat: { escapeString: false, printBasicPrototype: false, }, };

Custom Snapshot Serializers

// Add a custom serializer for a special object type expect.addSnapshotSerializer({ test: (val) => val && val.__type === 'Color', print: (val) => `Color(${val.r}, ${val.g}, ${val.b})`, }); test('color object', () => { expect({ __type: 'Color', r: 255, g: 0, b: 128 }) .toMatchInlineSnapshot(`Color(255, 0, 128)`); }); // jest.config.js — global serializers module.exports = { snapshotSerializers: [ 'enzyme-to-json/serializer', 'jest-serializer-html', './src/test-utils/mySerializer.js', ], }; // jest-styled-components serializer (popular with styled-components) import 'jest-styled-components'; test('styled button snapshot', () => { const tree = renderer.create(<StyledButton />).toJSON(); expect(tree).toMatchSnapshot(); // includes CSS in snapshot });

Async Components & Dynamic Values

import { render, screen, waitFor } from '@testing-library/react'; test('async data loads into component', async () => { const { asFragment } = render(<UserProfile userId="1" />); await waitFor(() => screen.getByText('Alice')); expect(asFragment()).toMatchSnapshot(); }); // Masking dynamic values with property matchers test('API response shape', () => { const response = { id: 123, createdAt: new Date().toISOString(), name: 'Bob' }; expect(response).toMatchSnapshot({ id: expect.any(Number), createdAt: expect.any(String), }); }); // Snapshot of serialized JSON test('serializes config', () => { const config = buildConfig({ env: 'test', debug: false }); expect(JSON.stringify(config, null, 2)).toMatchSnapshot(); });

Snapshot Best Practices

PracticeWhy
Commit .snap files to gitSnapshots are part of your test suite
Review snapshot diffs in PRsCatch unintentional UI regressions
Keep snapshots small & focusedLarge snapshots are hard to review
Use inline for simple valuesKeeps context in one file
Use property matchers for IDs/timestampsAvoid flaky tests from dynamic data
Avoid snapshots for logic testsUse explicit assertions for business logic