🪩

メモ:Next.js Route Handlers のモックテストについて

2024/12/17に公開
Changelog

メモ

雑なメモ書き。

APIエンドポイントのモックテスト

Node.jsサーバ向け

https://www.npmjs.com/package/node-mocks-http

DockerSwaggerPrism等々でモックサーバを立ち上げる必要がある。

Next.js向け

https://www.npmjs.com/package/next-test-api-route-handler

モックサーバを立ち上げる必要がない。

https://github.com/Xunnamius/next-test-api-route-handler/blob/edfe781e766cd174892cd394431eb307c134c3c5/src/index.ts#L403-L463

React/フロントエンド向け

https://www.npmjs.com/package/msw

モックサーバを立ち上げる必要がない。

https://developer.mozilla.org/docs/Web/API/Service_Worker_API

Storybookなど、フロントエンドを絡めたモックテストにとても有効。
ただし、モックハンドラーを用意したりの設定が必要。

Vitest公式ドキュメントで紹介されている方法がこれだったりする。

問題

  • モックサーバを立ち上げる必要がある
  • ライブラリ依存になる
  • バージョンの相性を考慮しなければならない
  • 単純なテストでは不要な、高機能すぎるが故の煩雑さ

実際にnext-test-api-route-handlernext@14.2.20以降で動かない事例がある。(記事執筆時点)

解決

NextRequestクラスRequestクラスで、リクエストのインスタンスを生成して各Route Handlersに渡す。

test/api/foo.test.ts
import { NextRequest } from "next/server";
import { describe, expect, it } from "vitest";
import { POST } from "@/app/api/route";
import type { Foo } from "@/models";
import { createAPIResponse } from "@/server";

describe("POST /api", () => {
  const MOCK_URL = "http://localhost:3000/api";

  it("正常なリクエストを処理できる", async () => {
    const validBody: Foo = {
      name: "foobar",
      occupationalStatus: "STUDENT",
      contactTool: "DISCORD",
      contactDetail: "foobar#1234",
    };
    const request = new NextRequest(MOCK_URL, {
      method: "POST",
      body: JSON.stringify(validBody),
    });
    const response = await POST(request);

    expect(response.status).toBe(200);
    expect(await response.json()).toMatchObject(createAPIResponse(validBody));
  });

  it("不正なメールアドレスの場合エラーを返す", async () => {
    const invalidBody: Foo = {
      name: "foobar",
      occupationalStatus: "STUDENT",
      contactTool: "EMAIL",
      contactDetail: "invalid-email",
    };
    const request = new NextRequest(MOCK_URL, {
      method: "POST",
      body: JSON.stringify(invalidBody),
    });
    const response = await POST(request);

    expect(response.status).toBe(400);
    const json = await response.json();
    expect(json.error).toBeDefined();
  });
});

createAPIResponse()別記事「TypeScriptやReactを書くときに意識していること」で紹介したハンドラーです。
レスポンスオブジェクトの生成をハンドラー化することで、テストも容易になります。

アプリ開発サークル@IPUT

Discussion