We use cookies to enhance your experience on the site
CodeWorlds

Snapshot Testing -- Constellation Photograph

Snapshot testing is a technique that involves "photographing" the rendering result of a component and comparing it with a saved reference. It's like taking a picture of a star constellation -- if any stars have shifted the next time you look, the test will detect it.

How Do Snapshots Work?

The mechanism is simple. On the first run of the test, Jest renders the component and saves the resulting HTML to a special

.snap
file in the
__snapshots__
folder. On subsequent runs, Jest re-renders the component and compares the result with the saved reference. If something changed -- the test fails. It's like comparing two constellation photos taken at different times -- if the stars have shifted, you'll notice immediately.

Here's a basic example:

1import { render } from '@testing-library/react';
2import StatusBadge from './StatusBadge';
3
4test('renders active status badge', () => {
5  const { container } = render(<StatusBadge status="active" />);
6  expect(container.firstChild).toMatchSnapshot();
7});

Generated Snapshot File (.snap)

1// Jest Snapshot v1
2
3exports[`renders active status badge 1`] = `
4<span
5  class="badge badge-active"
6>
7  Active
8</span>
9`;

Inline Snapshots

Instead of a separate file, the snapshot can be saved directly in the test:

1test('renders fuel display', () => {
2  const { container } = render(<FuelDisplay level={75} />);
3  expect(container.firstChild).toMatchInlineSnapshot(`
4    <div
5      class="fuel-display"
6    >
7      <span>
8        Fuel: 75%
9      </span>
10    </div>
11  `);
12});

Updating Snapshots

When you intentionally change a component, snapshots must be updated:

1# Update all snapshots
2npm test -- --updateSnapshot
3
4# Or shorthand
5npm test -- -u

When to Use Snapshots?

Good Use Cases

  • Presentational components -- simple components without logic
  • Resulting JSX tree -- checking HTML structure
  • Serializable data -- objects, arrays, JSON
1// Good snapshot -- simple component
2test('renders planet card', () => {
3  const { container } = render(
4    <PlanetCard name="Mars" distance="225M km" color="red" />
5  );
6  expect(container.firstChild).toMatchSnapshot();
7});

Bad Use Cases

  • Components with dynamic logic -- snapshot doesn't test behavior
  • Large components -- snapshots become hard to review
  • Components with dates/random IDs -- snapshot always changes
1// BAD snapshot -- too large, too dynamic
2test('renders entire dashboard', () => {
3  const { container } = render(<Dashboard />); // 500 lines of HTML!
4  expect(container).toMatchSnapshot(); // Nobody will read this!
5});

Snapshot vs. Targeted Assertions

This is a fundamental comparison that every React developer should understand. A snapshot checks the entire structure of a component -- every tag, class, and attribute. A targeted assertion checks specific behavior -- whether text is visible, whether a button is active, whether an element has the right class. The difference is like taking a photo of the entire control panel versus checking whether a specific indicator light is green.

1// Snapshot -- checks the ENTIRE structure
2expect(container.firstChild).toMatchSnapshot();
3
4// Targeted assertion -- checks SPECIFIC behavior (recommended!)
5expect(screen.getByText('Active')).toHaveClass('badge-active');
6expect(screen.getByRole('button')).toBeEnabled();

Rule: Prefer targeted assertions over snapshots. Snapshots easily become "invisible" -- developers often update them automatically with the

-u
command without checking what changed. Targeted assertions, on the other hand, require a conscious check of specific behavior. Snapshots are good as additional protection against unexpected visual changes, but should not be the main way of testing.

Snapshot Serializers

We can customize how snapshots are formatted:

1// In setupTests.js or config file
2expect.addSnapshotSerializer({
3  test: (val) => val && val.className,
4  print: (val) => `ClassName: ${val.className}`,
5});

Custom Snapshot Matchers

1test('renders navigation with correct structure', () => {
2  const { container } = render(<SpaceNavigation routes={routes} />);
3
4  // Snapshot of only part of the component
5  expect(
6    container.querySelector('.nav-links')
7  ).toMatchSnapshot();
8});

Summary -- When Snapshot, When Not?

| Situation | Snapshot | Targeted | |----------|----------|----------| | Simple UI component | Yes | Yes | | Business logic | No | Yes | | User interactions | No | Yes | | Data formatting | Yes | Yes | | Large component | No | Yes | | Visual regression | Yes | No |

Go to CodeWorlds