レビュー済み·難易度: 中級·更新: 2026-04-18

Jest で fetch をモックしてクライアントのデータ取得ロジックをテストする

jest.spyOn(global, 'fetch') または global.fetch のモック置換で、クライアント側のデータ取得関数の成功・エラー・ネットワーク障害ケースをテストする例。

nextjstestingjest

対応バージョン

nextjs 15react 19jest 29

前提環境

Jest の基本的な書き方(describe / it / expect)と TypeScript の型を理解していること

概要

fetch はブラウザ / Node.js のグローバル関数であり、Jest テスト内では jest.spyOn(global, "fetch") でモック化できる。成功レスポンス・HTTP エラー・ネットワーク障害の 3 ケースをテストすることで、クライアント側データ取得関数の堅牢性を検証する。

インストール

npm install --save-dev jest @types/jest ts-jest

実装

テスト対象の取得関数

// lib/api.ts
export type User = {
  id: number;
  name: string;
  email: string;
};

export async function fetchUser(id: number): Promise<User> {
  const res = await fetch(`/api/users/${id}`);

  if (!res.ok) {
    throw new Error(`HTTP error: ${res.status}`);
  }

  return res.json() as Promise<User>;
}

export async function fetchUsers(): Promise<User[]> {
  const res = await fetch("/api/users");

  if (!res.ok) {
    throw new Error(`HTTP error: ${res.status}`);
  }

  const data = (await res.json()) as { users: User[] };
  return data.users;
}

fetch モックテスト

// lib/api.test.ts
import { fetchUser, fetchUsers } from "./api";

// fetch レスポンスを生成するヘルパー
function mockResponse(body: unknown, status = 200): Response {
  return {
    ok: status >= 200 && status < 300,
    status,
    json: () => Promise.resolve(body),
  } as Response;
}

describe("fetchUser", () => {
  let fetchSpy: jest.SpyInstance;

  beforeEach(() => {
    fetchSpy = jest.spyOn(global, "fetch");
  });

  afterEach(() => {
    fetchSpy.mockRestore();
  });

  it("成功時はユーザーオブジェクトを返す", async () => {
    const user = { id: 1, name: "Alice", email: "alice@example.com" };
    fetchSpy.mockResolvedValue(mockResponse(user));

    const result = await fetchUser(1);

    expect(fetchSpy).toHaveBeenCalledWith("/api/users/1");
    expect(result).toEqual(user);
  });

  it("HTTP 404 のとき Error をスローする", async () => {
    fetchSpy.mockResolvedValue(mockResponse({ error: "Not Found" }, 404));

    await expect(fetchUser(999)).rejects.toThrow("HTTP error: 404");
  });

  it("ネットワーク障害のとき Error をスローする", async () => {
    fetchSpy.mockRejectedValue(new TypeError("Failed to fetch"));

    await expect(fetchUser(1)).rejects.toThrow("Failed to fetch");
  });
});

describe("fetchUsers", () => {
  let fetchSpy: jest.SpyInstance;

  beforeEach(() => {
    fetchSpy = jest.spyOn(global, "fetch");
  });

  afterEach(() => {
    fetchSpy.mockRestore();
  });

  it("成功時はユーザー配列を返す", async () => {
    const users = [
      { id: 1, name: "Alice", email: "alice@example.com" },
      { id: 2, name: "Bob", email: "bob@example.com" },
    ];
    fetchSpy.mockResolvedValue(mockResponse({ users }));

    const result = await fetchUsers();

    expect(result).toHaveLength(2);
    expect(result[0].name).toBe("Alice");
  });

  it("HTTP 500 のとき Error をスローする", async () => {
    fetchSpy.mockResolvedValue(mockResponse({ error: "Internal Server Error" }, 500));

    await expect(fetchUsers()).rejects.toThrow("HTTP error: 500");
  });
});

Jest 設定

// jest.config.ts
import type { Config } from "jest";

const config: Config = {
  preset: "ts-jest",
  testEnvironment: "node",
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/src/$1",
  },
};

export default config;

ポイント

  • jest.spyOn(global, "fetch")fetch をモックしつつ元の実装への参照を保持する。afterEachmockRestore() を呼ぶことで他テストへの影響を防ぐ
  • mockResolvedValuefetch が正常に Promise を解決するケース(HTTP エラーを含む)に使う。mockRejectedValue はネットワーク切断など fetch 自体が例外をスローするケースに使う
  • Responseok プロパティは status >= 200 && status < 300 のとき true になる。テスト用ヘルパーでこの挙動を再現することで、実際の Response オブジェクト生成を省略できる
  • テスト対象関数が fetch の URL 引数を正しく組み立てているか toHaveBeenCalledWith で検証する。パス構築のバグを早期に発見できる
  • global.fetch への代入(global.fetch = jest.fn())でも動作するが、spyOn の方が型安全で mockRestore による自動クリーンアップができるため推奨する

注意点

jest-component-test は React コンポーネントの DOM 操作テスト。jest-mock-module はモジュール依存の置換。これは fetch のグローバルモックで API 呼び出しを含む純粋な取得関数をテストするパターンに特化。jest.spyOn と mockResolvedValue / mockRejectedValue の使い分けを示す。

関連サンプル