A Practical Guide to Snapshot Testing

A Practical Guide to Snapshot Testing

January 6, 2025

Snapshot Testing
Jest
UI Testing
Testing Best Practices

Snapshot testing is a technique used primarily to verify that the output of a functionality, component, or module does not change unexpectedly. It is especially useful in software development projects involving user interfaces (UI), APIs, or complex data structures.

In this article, we will explore how snapshot testing works, its ideal use cases, when to avoid it, and how to implement inline snapshots using Jest with TypeScript and React.

What is Snapshot Testing?

Snapshot testing compares the current output of a component, function, or module with a previously saved reference ("snapshot"). If there are differences, the developer is notified to confirm whether these changes are intentional or not.

Advantages of Snapshot Testing

  • Easy to set up: Especially with tools like Jest.
  • Quick to detect regressions: Identifies unexpected changes in the code.
  • Ideal for UIs: Verifies that components do not change visually or structurally without reason.

Limitations of Snapshot Testing

  • Hard to interpret: Large snapshots can be challenging to understand.
  • Over-reliance: Encourages automatic updates without understanding changes.

When to Use Snapshot Testing

Ideal Use Cases:

  1. UI Components: Verifying that a React component’s structure doesn’t change unexpectedly.
  2. Complex Data Structures: Ensuring that objects or data generated by functions don’t suffer accidental changes.
  3. Static Outputs: Validating that specific static or configuration values remain constant.

When to Avoid It:

  1. Logical Tests: Traditional unit tests are clearer and more maintainable for pure logic.
  2. Volatile Data: If the output changes constantly (e.g., timestamps or random values).
  3. Large Snapshots: Difficult to review and understand.

Implementing Snapshot Testing with Jest, React, and TypeScript

Testing a Navigation Bar Component

Let’s consider a Navbar component that displays a navigation bar with a couple of links:

// Navbar.tsx
import React from "react";
 
type NavbarProps = {
  links: { href: string; label: string }[];
};
 
export const Navbar: React.FC<NavbarProps> = ({ links }) => {
  return (
    <nav className="navbar">
      <ul>
        {links.map((link, index) => (
          <li key={index}>
            <a href={link.href}>{link.label}</a>
          </li>
        ))}
      </ul>
    </nav>
  );
};

Here’s a snapshot test for this component:

// Navbar.test.tsx
import React from "react";
import { render } from "@testing-library/react";
import { Navbar } from "./Navbar";
 
test("Navbar matches snapshot", () => {
  const { asFragment } = render(
    <Navbar
      links={[
        { href: "/home", label: "Home" },
        { href: "/about", label: "About" },
      ]}
    />
  );
  expect(asFragment()).toMatchSnapshot();
});

This test ensures that any structural or visual changes to the Navbar component are intentional.

Generated Snapshot File

After running the test, Jest will generate a snapshot file in the snapshots directory. The content of the file will look like this:

// Navbar.test.tsx.snap
exports[`Navbar matches snapshot 1`] = `
<DocumentFragment>
  <nav
    class="navbar"
  >
    <ul>
      <li>
        <a
          href="/home"
        >
          Home
        </a>
      </li>
      <li>
        <a
          href="/about"
        >
          About
        </a>
      </li>
    </ul>
  </nav>
</DocumentFragment>
`;

This file stores the structure of the Navbar component at the time the test was run.

How to Create Inline Snapshots

Jest also allows snapshots to be stored directly in the test file. This is useful when snapshots are small and easy to read.

// Inline snapshot example
import React from "react";
import { render } from "@testing-library/react";
import { Navbar } from "./Navbar";
 
test("Navbar matches inline snapshot", () => {
  const { asFragment } = render(
    <Navbar
      links={[
        { href: "/home", label: "Home" },
        { href: "/about", label: "About" },
      ]}
    />
  );
  expect(asFragment()).toMatchInlineSnapshot(`
    <DocumentFragment>
      <nav
        class="navbar"
      >
        <ul>
          <li>
            <a
              href="/home"
            >
              Home
            </a>
          </li>
          <li>
            <a
              href="/about"
            >
              About
            </a>
          </li>
        </ul>
      </nav>
    </DocumentFragment>
  `);
});

When to Use Inline Snapshots

  • Small Snapshots: When the snapshot content is short and easy to understand.
  • Quick Reviews: Allows changes to be seen directly in the test file without looking for a separate file.

Updating Snapshots

If the snapshot change is intentional, you can update it by running:

jest --updateSnapshot

This will update the saved snapshots (inline or in separate files).

Best Practices

  1. Keep Snapshots Small and Readable: Avoid saving unnecessarily large snapshots.
  2. Review Changes: Always review snapshot changes to understand what has changed and why.
  3. Combine with Other Tests: Use traditional unit tests for logic and snapshots for structure.

Conclusion

Snapshot testing is a powerful tool for detecting regressions in software projects, particularly in UI components. However, it should be used sparingly and in the right cases. With Jest and TypeScript, you can easily implement this technique using either traditional or inline snapshots, depending on the context. Remember to combine snapshot testing with other testing strategies to ensure the quality of your code.


Further Resources


Snapshot testing can be a game-changer when used appropriately. By understanding its strengths, limitations, and best practices, you can create reliable and maintainable tests that safeguard your software's consistency over time.

Happy testing! 🎉


Thanks for reading me. 😊

Buy Me A Coffee