nock の使い方

HTTP server mocking and expectations library for Node.js

v14.0.12/週MITテスト
AI生成コンテンツ

この記事はAIによって生成されました。内容の正確性は保証されません。最新の情報は公式ドキュメントをご確認ください。

nock の使い方 — Node.jsのHTTPモックライブラリ完全ガイド

一言でいうと

nockは、Node.jsのhttp.requestをオーバーライドすることで、外部HTTPリクエストをインターセプトし、任意のレスポンスを返すテスト用モックライブラリです。実際のネットワーク通信を行わずに、HTTPクライアントの振る舞いを検証できます。

どんな時に使う?

  • 外部APIに依存するモジュールのユニットテスト — GitHub API、AWS SDK、Stripe APIなど、外部サービスへのHTTPリクエストを含むコードを、ネットワーク接続なしでテストしたい場合
  • エラーハンドリングの検証 — タイムアウト、500エラー、ネットワーク障害など、本番環境では再現しにくい異常系レスポンスをシミュレートしたい場合
  • リクエスト内容のアサーション — 自分のコードが正しいURL・ヘッダー・ボディでHTTPリクエストを送信しているかを検証したい場合

インストール

# npm
npm install --save-dev nock

# yarn
yarn add --dev nock

# pnpm
pnpm add -D nock

注意: nock v14.x は現在メンテナンスされているNode.jsバージョンをサポートしています。

基本的な使い方

最もよく使うパターンは、特定のURLへのリクエストをインターセプトし、固定のレスポンスを返すケースです。

import nock from 'nock';
import axios from 'axios';

// テストのセットアップ
const scope = nock('https://api.github.com')
  .get('/repos/atom/atom/license')
  .reply(200, {
    license: {
      key: 'mit',
      name: 'MIT License',
      spdx_id: 'MIT',
    },
  });

// テスト対象のコードを実行
const response = await axios.get(
  'https://api.github.com/repos/atom/atom/license'
);

console.log(response.data.license.key); // => 'mit'

// すべてのインターセプタが消費されたか確認
scope.isDone(); // => true

重要な仕組み: nockで定義したインターセプタは、一度使用されると自動的に削除されます。つまり、同じURLへのリクエストが2回あるなら、インターセプタも2つ定義する必要があります。

よく使うAPI

1. nock().reply() — レスポンスの定義

最も基本的なAPIです。ステータスコード、ボディ、ヘッダーを指定できます。

import nock from 'nock';

// 静的なレスポンス
nock('https://api.example.com')
  .get('/users/1')
  .reply(200, { id: 1, name: 'Alice' }, { 'X-Custom-Header': 'value' });

// 関数でレスポンスを動的に生成
nock('https://api.example.com')
  .post('/users')
  .reply(201, (uri: string, requestBody: Record<string, unknown>) => {
    return { id: 999, ...requestBody };
  });

2. nock().persist() — インターセプタの永続化

デフォルトでは1回使うと消えるインターセプタを、何度でも使えるようにします。

import nock from 'nock';

const scope = nock('https://api.example.com')
  .get('/health')
  .reply(200, { status: 'ok' })
  .persist();

// 何度リクエストしても同じレスポンスが返る
await fetch('https://api.example.com/health'); // 200
await fetch('https://api.example.com/health'); // 200(消えない)

// テスト後にクリーンアップ
scope.persist(false);
nock.cleanAll();

3. リクエストボディ・クエリのマッチング

POSTボディやクエリパラメータを条件に含めることができます。

import nock from 'nock';

// リクエストボディのマッチング
nock('https://api.example.com')
  .post('/login', { username: 'admin', password: 'secret' })
  .reply(200, { token: 'abc123' });

// 正規表現でボディをマッチング
nock('https://api.example.com')
  .post('/login', /admin/)
  .reply(200, { token: 'abc123' });

// クエリパラメータのマッチング
nock('https://api.example.com')
  .get('/users')
  .query({ page: 1, limit: 10 })
  .reply(200, [{ id: 1, name: 'Alice' }]);

// クエリパラメータを正規表現やbooleanで柔軟にマッチング
nock('https://api.example.com')
  .get('/search')
  .query(true) // 任意のクエリパラメータを許容
  .reply(200, []);

4. エラーレスポンスとネットワークエラーのシミュレーション

異常系テストに不可欠な機能です。

import nock from 'nock';

// HTTPエラーステータス
nock('https://api.example.com')
  .get('/not-found')
  .reply(404, { error: 'Not Found' });

// ネットワークエラー(接続失敗)
nock('https://api.example.com')
  .get('/timeout')
  .replyWithError('ETIMEDOUT');

// 構造化されたエラーオブジェクト
nock('https://api.example.com')
  .get('/connection-reset')
  .replyWithError({
    message: 'Connection reset',
    code: 'ECONNRESET',
  });

// レスポンス遅延のシミュレーション
nock('https://api.example.com')
  .get('/slow')
  .delay(5000) // 5秒遅延
  .reply(200, { data: 'finally' });

// 接続遅延とボディ遅延を個別に指定
nock('https://api.example.com')
  .get('/very-slow')
  .delayConnection(2000) // 接続に2秒
  .delayBody(3000) // ボディ送信に3秒
  .reply(200, { data: 'done' });

5. nock.cleanAll() / nock.restore() — クリーンアップ

テスト間の干渉を防ぐために、適切なクリーンアップが重要です。

import nock from 'nock';

// すべてのインターセプタを削除
nock.cleanAll();

// nockによるhttp.requestのオーバーライドを完全に解除
nock.restore();

// 再度有効化する場合
if (!nock.isActive()) {
  nock.activate();
}

// 実際のHTTPリクエストを禁止(モック漏れの検出に有用)
nock.disableNetConnect();

// localhostだけは許可(テストサーバー用)
nock.disableNetConnect();
nock.enableNetConnect('127.0.0.1');

// すべてのHTTPリクエストを再度許可
nock.enableNetConnect();

類似パッケージとの比較

特徴nockmswsinon (fake server)
動作レイヤーhttp.requestをオーバーライドService WorkerまたはリクエストインターセプトXMLHttpRequestをモック
ブラウザ対応❌ Node.js専用✅ ブラウザ + Node.js✅ ブラウザ中心
fetch 対応✅(Node.jsのfetch)
セットアップの手軽さ◎ 非常にシンプル○ やや設定が必要
リクエスト記録✅ 内蔵
型安全性
未マッチリクエストの検出
主な用途Node.jsのユニットテストフルスタックのインテグレーションテストブラウザ中心のテスト

選定の目安:

  • Node.js専用のユニットテストなら nock が最もシンプルで実績豊富
  • ブラウザとNode.jsの両方で統一的にモックしたいなら msw
  • 既にsinonを使っているプロジェクトなら sinon のfake serverも選択肢

注意点・Tips

インターセプタは使い捨て

最も多いハマりポイントです。nockのインターセプタはデフォルトで1回使うと消えます。

// ❌ 2回目のリクエストでエラーになる
nock('https://api.example.com').get('/data').reply(200, { ok: true });

await fetch('https://api.example.com/data'); // ✅ 成功
await fetch('https://api.example.com/data'); // ❌ エラー!

// ✅ 複数回呼ばれる場合は .persist() か .times() を使う
nock('https://api.example.com')
  .get('/data')
  .times(3) // 3回まで
  .reply(200, { ok: true });

テストの後始末を忘れない

import nock from 'nock';

afterEach(() => {
  nock.cleanAll(); // 未使用のインターセプタを削除
});

afterAll(() => {
  nock.restore(); // http.requestを元に戻す
});

nock.disableNetConnect() でモック漏れを検出

テスト中に意図しない実HTTPリクエストが発生するのを防げます。

beforeAll(() => {
  nock.disableNetConnect();
  // ローカルのテストサーバーだけ許可する場合
  nock.enableNetConnect(/127\.0\.0\.1|localhost/);
});

afterAll(() => {
  nock.enableNetConnect();
});

scope.isDone() でインターセプタの消費を検証

定義したモックがすべて呼ばれたかを確認できます。テストの信頼性向上に有効です。

const scope = nock('https://api.example.com')
  .get('/users')
  .reply(200, []);

// テスト対象のコードを実行
await myFunction();

// すべてのインターセプタが使われたか検証
expect(scope.isDone()).toBe(true);

// 未消費のインターセプタを確認
console.log(scope.pendingMocks()); // => [] なら全部消費済み

ESModulesでの注意

ESModulesから直接httpをインポートしているコードでは、nockのインターセプトが効かない場合があります。これはESModulesのライブバインディングの仕組みに起因します。該当する場合は、HTTPクライアントライブラリ(axios, node-fetchなど)経由でリクエストするか、mswへの移行を検討してください。

Jestとのメモリ問題

Jestと組み合わせる場合、--maxWorkers を制限するか、テストファイルごとにnock.cleanAll()を確実に呼ぶことで、メモリリークを防げます。

まとめ

nockは、Node.jsにおけるHTTPモックのデファクトスタンダードとして長年使われてきた信頼性の高いライブラリです。nock(url).get(path).reply(status, body) という直感的なAPIで、外部API依存のコードを簡単にテストできます。インターセプタの使い捨て仕様とcleanAll()によるクリーンアップさえ押さえておけば、堅牢なテストスイートを構築できるでしょう。