レビュー待ち·難易度: 初級·更新: 2026-04-18

Jest でスナップショットテストを書いてUI回帰を防ぐ

toMatchSnapshot / toMatchInlineSnapshot でコンポーネントの構造的回帰を検出する例。スナップショットの更新・管理方法とインラインスナップショットの使い分けも示す。

nextjstestingjest

対応バージョン

nextjs 15react 19jest 29

前提環境

Jest の基本的なテストの書き方と React コンポーネントの書き方を理解していること

概要

toMatchSnapshot でコンポーネントのレンダリング結果をファイルに保存し、以降の変更で構造が意図せず変わっていないかを自動検出する。toMatchInlineSnapshot はスナップショットをテストファイル内にインライン保存するため、小さなコンポーネントに適している。

インストール

npm install jest @types/jest ts-jest
npm install --save-dev @testing-library/react @testing-library/jest-dom jest-environment-jsdom

実装

テスト対象コンポーネント

// components/Badge.tsx
type Props = {
  label: string;
  variant?: "default" | "success" | "error";
};

export function Badge({ label, variant = "default" }: Props) {
  const colorMap = {
    default: "bg-gray-100 text-gray-700",
    success: "bg-green-100 text-green-700",
    error: "bg-red-100 text-red-700",
  };

  return (
    <span className={`inline-block rounded px-2 py-0.5 text-xs font-medium ${colorMap[variant]}`}>
      {label}
    </span>
  );
}

toMatchSnapshot(外部ファイルに保存)

// components/Badge.test.tsx
import { render } from "@testing-library/react";
import { Badge } from "./Badge";

describe("Badge スナップショット", () => {
  it("default バリアントのスナップショット", () => {
    const { container } = render(<Badge label="タグ" />);
    expect(container.firstChild).toMatchSnapshot();
  });

  it("success バリアントのスナップショット", () => {
    const { container } = render(<Badge label="完了" variant="success" />);
    expect(container.firstChild).toMatchSnapshot();
  });

  it("error バリアントのスナップショット", () => {
    const { container } = render(<Badge label="エラー" variant="error" />);
    expect(container.firstChild).toMatchSnapshot();
  });
});

初回実行時に __snapshots__/Badge.test.tsx.snap が自動生成される:

// __snapshots__/Badge.test.tsx.snap
exports[`Badge スナップショット default バリアントのスナップショット 1`] = `
<span
  class="inline-block rounded px-2 py-0.5 text-xs font-medium bg-gray-100 text-gray-700"
>
  タグ
</span>
`;

toMatchInlineSnapshot(テストファイル内にインライン保存)

// components/Badge.inline.test.tsx
import { render } from "@testing-library/react";
import { Badge } from "./Badge";

it("Badge のインラインスナップショット", () => {
  const { container } = render(<Badge label="完了" variant="success" />);
  // 初回実行後、Jest が自動でスナップショット文字列を埋め込む
  expect(container.firstChild).toMatchInlineSnapshot(`
    <span
      class="inline-block rounded px-2 py-0.5 text-xs font-medium bg-green-100 text-green-700"
    >
      完了
    </span>
  `);
});

スナップショットの更新

# スナップショットを意図的に更新する
npx jest --updateSnapshot
# または
npx jest -u

ポイント

  • toMatchSnapshot は初回実行でスナップショットを生成し、2回目以降は差分を検出する。コンポーネントを意図的に変更したときは jest -u で更新する
  • toMatchInlineSnapshot はスナップショット文字列をテストファイル内に保存するため、外部ファイルを参照せずにレビューできる。小さなコンポーネントや1〜2個のケースに適している
  • スナップショットは構造の回帰防止に適しているが、実装詳細(className の文字列など)に強く依存するため、リファクタリングごとに更新が必要になる点に注意する
  • jest-component-test との使い分け: ユーザー操作・状態変化の検証には render + userEvent、レンダリング結果の構造変化の検出にはスナップショットを使う
  • スナップショットテストはレビュー時に差分を確認できることが価値。意味のない大量スナップショットは管理コストが増えるため、重要な UI 部品に絞って使う

注意点

jest-component-test は render + userEvent でインタラクションを検証する。これは toMatchSnapshot / toMatchInlineSnapshot でレンダリング結果の構造的変化を自動検出するパターン。

関連サンプル