0
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 (Edge Runtime) の Route Handler テストでつまずいた話と、その解決策

Last updated at Posted at 2025-01-28

テストを書いたことのあるプログラマーなら一度は「なんでテストが通らないんだ…?」と頭を抱えた経験があると思います。今回、私もまさにその状態に陥りました。それは、Next.js の Edge Runtime を使ったプロジェクトでのこと。Route Handler をテストしようとしたとき、あの忌々しい「is not a function」エラーに出くわしました。このエラーを克服するために試行錯誤した経験を、ここで共有します。同じ問題に悩む方の助けになれば幸いです。


問題に直面したときの状況

私が取り組んでいたのは、Next.js の Route Handler のテストでした。特に Edge Runtime を使う際には、Node.js 標準の Response と Next.js 独自の拡張 Response の間で競合が起こることがあります。その結果、NextResponse.json() を呼び出した瞬間に「is not a function」というエラーが発生してしまいました。

さらに、Jest を使ったテスト環境では、モジュールの読み込み順や jest.mock() の使い方次第で新たな問題が発生することがありました。このような細かいトラップがいくつも重なり、初めは手詰まり状態でした。


私が実践した解決策

いろいろ試してみた結果、以下の方法を組み合わせることで問題を解決できました。それぞれのアプローチを詳しく説明します。

1. Edge Runtime 対応のテスト環境を使用する

まず最初にやったのは、Jest のテスト環境を Edge Runtime に近づけることです。具体的には、@edge-runtime/jest-environmenttestEnvironment に設定しました。これを導入することで、Next.js 独自の Web API 実装と Node.js のモック環境との競合を減らすことができました。

// jest.config.js の例
{
  "testEnvironment": "@edge-runtime/jest-environment"
}

これだけで、「is not a function」エラーの発生頻度がかなり減りました。


2. 過剰なモックを減らす

テストをシンプルに保つことも重要でした。Edge Runtime 対応のテスト環境を使う場合、Response.json() のモックは不要です。むしろ余計なモックを追加すると、新たな問題を引き起こす原因になりました。テスト対象の範囲を明確にし、必要最低限のモックだけを使うことで、問題を切り分けやすくなりました。


3. ビジネスロジックと Route Handler の分離

私が抱えていた最大の問題は、テスト対象の範囲が広すぎたことでした。Route Handler の中にビジネスロジックを直接埋め込んでしまっていたため、テストが複雑化していたのです。

そこで、ビジネスロジックを独立したクラスや関数に切り出しました。これにより、ビジネスロジックそのものは単体テストでカバーし、Route Handler 側ではそのロジックをモックするだけで済むようになりました。


4. モックの読み込み順序を明確化

Jest で ES Modules を使う際には、モジュールの読み込み順が非常に重要です。jest.mock() を使う場合は、import 文よりも前に記述しなければモックが効きません。また、テスト間でモジュールキャッシュの影響を受けないよう、jest.resetModules()jest.isolateModules() を活用しました。


5. パスエイリアスでモジュール管理を簡素化

プロジェクトの規模が大きくなると、相対パスが複雑になりがちです。そのため、tsconfig.jsonmoduleNameMapper を活用して、パスエイリアスを設定しました。これにより、モジュール間のパス管理がスムーズになり、テストコードの可読性も向上しました。


実際のコード例

具体例を挙げると、次のようにビジネスロジックと Route Handler のテストを分離しました。

ビジネスロジックの単体テスト

// line-login-service.test.ts
describe("LineLoginService", () => {
  beforeEach(() => {
    global.fetch = jest.fn();
  });

  it("APIのURLが設定されていない場合", async () => {
    const service = new LineLoginService("");
    const result = await service.getLoginUrl();
    expect(result).toEqual({ error: "API URLが設定されていません" });
  });
});

Route Handler のテスト(ビジネスロジックをモック)

// route.test.ts
jest.mock("@line-login-service", () => ({
  LineLoginService: jest.fn().mockImplementation(() => ({
    getLoginUrl: jest.fn().mockResolvedValue({ url: "https://line.login.url" }),
  })),
}));

describe("LINE Login Route Handler", () => {
  it("正常にURLを返す", async () => {
    const response = await GET();
    const body = await response.json();
    expect(body).toEqual({ url: "https://line.login.url" });
  });
});

最後に

今回の経験を通じて、「テストはシンプルであるべき」という基本的な考え方の大切さを改めて感じました。また、Edge Runtime のような特殊な環境でのテストは独特な課題が伴いますが、環境を整えるだけでも問題解決の糸口が見えてきます。

もし同じような問題に直面している方がいたら、この経験が参考になれば嬉しいです。そして、「まだこうした方が良いよ」というフィードバックがあれば、ぜひ教えてください!

0
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
0
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?