2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Next.js】Jest でのテスト方法完全ガイド - モック関数からサーバーコンポーネントまで

Last updated at Posted at 2025-04-30

はじめに

この記事では、Next.js プロジェクトでの Jest を使ったテスト方法について解説します。モック関数の基本から、サーバーコンポーネントのテストまで、実践的なテクニックをまとめています。

他にもこういう方法あるよ!などアドバイスがあればコメントいただきたいです🙏

目次

モック関数の基本

モック化

const handleClick = jest.fn()

関数が呼ばれたことの検証

// 関数が呼び出されたかどうかを確認
expect(handleClick).toHaveBeenCalled()

// 関数が指定回呼び出されたかを確認
expect(handleClick).toHaveBeenCalledTimes(2)

// 関数が特定の引数で呼び出されたかを確認
expect(handleClick).toHaveBeenCalledWith('引数')

モック関数の戻り値設定

mockReturnValue

mock関数を呼び出した際の返り値を指定する際はmockReturnValueを使います。

describe("jest.fn()",()=>{
  it("mockReturnValue",()=>{
      const mockFunction = jest.fn().mockReturnValue("Hello mock")
      console.log(mockFunction()) // "Hello mock"が出力される
   })
})

mockResolveValue

mock関数の返り値として、Promise(resolve)を設定したい場合はmockResolvedValueを使います。

主に非同期APIのモック化に利用されます。

const sampleFn = jest.fn().mockResolvedValue({
  a: 1
})

expect(sampleFn()).resolves.toEqual({
  a: 1
})

mockRejectValue

mock関数の返り値として、Promise(reject)を返したい場合はmockRejectedValueを使います。

APIエラーのテストなどで用いられます。

const sampleFn = jest.fn().mockRejectedValue({
  error: "データの取得に失敗しました"
})

expect(sampleFn()).rejects.toEqual({
  error: "データの取得に失敗しました"
})

モック関数のリセット

mockClear

mockClear はモック関数の呼び出し履歴(.mock.calls.mock.results など)をリセットします。

ただし、モック関数の動作自体(返り値など)はリセットされません。テストケース間でモック関数の呼び出し履歴をクリアしたい場合に使用します。

const mockFn = jest.fn().mockReturnValue("テスト");
mockFn();
expect(mockFn.mock.calls.length).toBe(1); // 呼び出し回数は1

mockFn.mockClear();
expect(mockFn.mock.calls.length).toBe(0); // 呼び出し回数は0にリセット
expect(mockFn()).toBe("テスト"); // 返り値は変わらない

mockReset

mockResetmockClear の機能に加えて、モック関数の動作(返り値など)もリセットします。

const mockFn = jest.fn().mockReturnValue("テスト");
console.log(mockFn()); // "テスト"

mockFn.mockReset();
console.log(mockFn()); // undefined

.resolves / .rejects

expect 宣言で .resolves マッチャを使うこともでき、Jestはそのpromiseが解決されるまで待機します。
promisereject された場合は、テストは自動的に失敗します。

test('the data is peanut butter', () => {
  return expect(fetchData()).resolves.toBe('peanut butter');
});

タイマー機能のモック

setTimeoutsetInterval などの時間に関わる関数をテストする場合、Jestはタイマーをモック化する機能を提供しています。

// jest.useFakeTimers   偽のタイマーを有効化
// jest.advanceTimersByTime   指定した時間分タイマーを進める
// jest.useRealTimers タイマーを通常の動作に戻す(useFakeTimersはすべてのタイマーの動作に影響するためテスト完了後に必ず実行する)

describe('タイマーテスト', () => {
  beforeEach(() => {
    jest.useFakeTimers(); // 偽のタイマーを有効化
  });

  afterEach(() => {
    jest.useRealTimers(); // テスト後に通常の動作に戻す
  });

  test('500ms後にコールバックが実行される', () => {
    const callback = jest.fn();
    setTimeout(callback, 500);
    expect(callback).not.toHaveBeenCalled();
    
    // 時間を500ms進める
    jest.advanceTimersByTime(500);
    expect(callback).toHaveBeenCalledTimes(1);
  });
});

コンポーネントのモック

コンポーネントをモック化したい場合、関数としてモック化します。

// Searchコンポーネントをモック化
jest.mock('@/components/Search', () => {
  return function MockSearchBox() {
    return <div data-testid="search-box">SearchBox</div>
  }
});

it('検索ボックスが表示される', () => {
  render(<PageWithSearchBox />);
  expect(screen.getByTestId('search-box')).toBeInTheDocument();
});

コンポーネントの export によってmock方法が変わります。

export default でエクスポートされている場合は上記のように関数を直接返せますが、export const でエクスポートされている場合は、以下のようにオブジェクトとして返す必要があります。

jest.mock('@/components/Search', () => ({
  SearchBox: function MockSearchBox() {
    return <div data-testid="search-box">SearchBox</div>;
  }
}));

ライブラリの戻り値をモック

APIやサービスの呼び出し結果をモック化したい場合に使用します。

jest.mock('@/lib/api', () => ({
  // getArticlesは常に特定の記事一覧を返すようにする
  getArticles: () => Promise.resolve([
    { id: 1, title: 'テスト記事1' },
    { id: 2, title: 'テスト記事2' }
  ]),
  // createArticleは成功レスポンスを返すようにする
  createArticle: () => Promise.resolve({ success: true, id: 999 })
}));

it('記事一覧が表示される', async () => {
  render(<ArticleList />);
  expect(await screen.findByText('テスト記事1')).toBeInTheDocument();
  expect(screen.getByText('テスト記事2')).toBeInTheDocument();
});

サーバーコンポーネントのテスト

Next.js 13以降のサーバーコンポーネントは非同期関数として実装されるため、テストでは await を使ってレンダリングする必要があります。

it('Server Components', async () => {
  render(await ServerComponent());
  
  expect(screen.getByText('サーバーコンポーネント')).toBeInTheDocument();
});

useRouter, useSearchParams をモック化

Next.jsuseRouteruseSearchParams などのフックをモック化する方法です。

import { useRouter, useSearchParams } from "next/navigation";

// next/navigationモジュールをモック化
jest.mock("next/navigation", () => ({
  useSearchParams: jest.fn(),
  useRouter: jest.fn(),
}));

// テスト内でモックの戻り値を設定
beforeEach(() => {
  // useSearchParamsの戻り値を設定
  (useSearchParams as jest.Mock).mockReturnValue({
    get: jest.fn().mockReturnValue(''),
  });

  // useRouterの戻り値を設定
  (useRouter as jest.Mock).mockReturnValue({
    push: jest.fn(),
  });
});

特定のテストケースで条件を切り替える

APIレスポンスに応じたUIの表示をテストする例です。
beforeEach を使って各テストケースで異なるモックを設定しています。

import { render, screen } from "@testing-library/react";
import MyPostsList from "./MyPostsList";

// API関数をモック化
jest.mock('@/app/(private)/account/fetcher', () => ({
  getMyPosts: jest.fn()
}));

describe('初期表示', () => {
  beforeEach(() => {
    // 記事一覧が返されるようにモック設定
    (getMyPosts as jest.Mock).mockResolvedValue({
      json: jest.fn().mockResolvedValue({
        posts: [
          {
            id: 1,
            title: 'テストタイトル1',
            published: true,
          },
        ],
      }),
    });
  });

  it('記事の内容が表示される', async () => {
    render(await MyPostsList({ currentPage: 1, perPage: 10 }));
    expect(screen.getByText('テストタイトル1')).toBeInTheDocument();
  });
});

describe('記事が存在しない場合', () => {
  beforeEach(() => {
    // モック化されたモジュールにアクセスする
    const { getMyPosts } = jest.requireMock("@/app/(private)/account/fetcher");
    // 空の記事一覧が返されるようにモック設定
    (getMyPosts as jest.Mock).mockResolvedValue({
      json: jest.fn().mockResolvedValue({
        posts: [],
      }),
    });
  });

  it('記事がありませんと表示される', async () => {
    render(await MyPostsList({ currentPage: 1, perPage: 10 }));
    expect(screen.getByText('記事がありません。')).toBeInTheDocument();
  });
});

まとめ

Next.jsアプリケーションでJestを使ったテストを効率的に行うための主要なテクニックを紹介しました。

(Vibe Coding で書くことが多かったので、理解が不足している点が多かった...)

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?