axios-mock-adapter vs faker vs json-server vs miragejs vs msw vs nock
フロントエンド開発における API モックとデータ生成ツールの比較
axios-mock-adapterfakerjson-servermiragejsmswnock類似パッケージ:

フロントエンド開発における API モックとデータ生成ツールの比較

本比較では、フロントエンド開発およびテストにおいて HTTP リクエストをインターセプトし、モックレスポンスを返すための主要なライブラリ群を扱います。mswmiragejs はアプリケーションレベルでのモック化を、axios-mock-adapternock は特定の HTTP クライアントやノード環境でのインターセプトを、json-server は設定ファイルベースの簡易サーバーを、faker は偽データ生成をそれぞれ担当します。開発フェーズ(プロトタイピング、結合テスト、ユニットテスト)や実行環境(ブラウザ、Node.js)に応じて適切なツールを選定するための指針を提供します。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
axios-mock-adapter03,55167.9 kB961年前MIT
faker0-10.1 MB--MIT
json-server075,67639 kB7072日前MIT
miragejs05,5312.29 MB2132年前MIT
msw017,7634.92 MB424日前MIT
nock013,091185 kB881ヶ月前MIT

フロントエンド開発における API モックとデータ生成ツールの比較

現代のフロントエンド開発において、バックエンド API の完成を待たずに開発を進めることや、テスト環境で安定したレスポンスを確保することは不可欠です。mswmiragejsaxios-mock-adapternockjson-serverfaker は、それぞれ異なるアプローチでこの課題を解決しようとします。本稿では、これらのツールがどのように動作し、どの状況で選ぶべきかを技術的な観点から深掘りします。

🌐 リクエストインターセプトの仕組み:ネットワークレベル vs ライブラリレベル

モックツールの選定で最も重要なのは、どこでリクエストを捉えるかという点です。これにより、テストの信頼性とセットアップの手間が決まります。

msw は Service Worker を利用し、ブラウザのネットワークレベルでリクエストをインターセプトします。

  • アプリケーションコード(fetchaxios)を変更せずにモックできます。
  • ブラウザ開発ツールのネットワークタブで実際の通信として表示されます。
// msw: Service Worker によるネットワークインターセプト
import { http, HttpResponse } from 'msw';
import { setupWorker } from 'msw/browser';

const worker = setupWorker(
  http.get('/api/users', () => {
    return HttpResponse.json({ id: 1, name: 'Alice' });
  })
);
worker.start();

miragejs は、アプリケーション内に仮想サーバーを構築し、XMLHttpRequest や Fetch をフックします。

  • ブラウザ内で完結するため、外部サーバーへの依存がありません。
  • 独自の ORM を持ち、データの状態管理が可能です。
// miragejs: アプリケーション内仮想サーバー
import { createServer } from 'miragejs';

createServer({
  routes() {
    this.get('/api/users', () => {
      return { id: 1, name: 'Alice' };
    });
  }
});

axios-mock-adapter は、Axios ライブラリ内部のインターセプターを利用します。

  • Axios を使用している場合のみ機能します。
  • ネットワークレベルではなく、ライブラリ呼び出し段階でレスポンスを返します。
// axios-mock-adapter: Axios インターセプター
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';

const mock = new MockAdapter(axios);
mock.onGet('/api/users').reply(200, { id: 1, name: 'Alice' });

nock は、Node.js 環境で HTTP ソケットレベルをインターセプトします。

  • ブラウザでは動作せず、テストランナー(Jest 等)上で動作します。
  • 実際の HTTP リクエストが外部に飛ばないことを保証します。
// nock: Node.js HTTP ソケットインターセプト
import nock from 'nock';

nock('http://api.example.com')
  .get('/users')
  .reply(200, { id: 1, name: 'Alice' });

json-server は、実際の Node.js サーバーを起動し、ファイルベースでレスポンスを返します。

  • インターセプトではなく、実際の HTTP サーバーとして動作します。
  • 別プロセスで起動するため、アプリケーションとは独立しています。
# json-server: CLI によるサーバー起動
echo '{ "users": [ { "id": 1, "name": "Alice" } ] }' > db.json
npx json-server --watch db.json

faker はリクエストインターセプト機能を持ちません。

  • 偽データを生成するためのユーティリティライブラリです。
  • 上記ツールと組み合わせてレスポンスデータを生成するために使用されます。
// faker: 偽データ生成(※非推奨パッケージ)
import faker from 'faker';

// 注意:このパッケージはメンテナンス終了済みです
const name = faker.name.findName(); 

🛠️ 実行環境と対応範囲:ブラウザ vs Node.js

ツールの動作環境は、テスト戦略に直結する重要な要素です。

  • msw: ブラウザと Node.js の両方に対応。ユニバーサルなモック戦略が可能です。
  • miragejs: 主にブラウザ環境を想定。Node での使用は限定的です。
  • axios-mock-adapter: 環境を問いませんが、Axios 依存です。
  • nock: Node.js 専用。ブラウザテストには使用できません。
  • json-server: Node.js 上で動作する独立サーバー。
  • faker: 環境を問いませんが、パッケージ自体が非推奨です。

開発者が特に注意すべきは nock です。フロントエンドのユニットテストをブラウザ環境(Vitest, Jest jsdom)で実行する場合、nock は機能しません。一方、msw は環境を選ばないため、テストインフラの統一に適しています。

📦 データ生成と管理:静的データ vs 動的生成

モックデータが静的か動的かも、ツールの選定理由になります。

json-server は静的な JSON ファイルに依存します。

  • データの変更にはファイル編集が必要です。
  • 簡易的な CRUD 操作はサポートされています。
// json-server: db.json
{
  "posts": [
    { "id": 1, "title": "Hello" }
  ]
}

miragejsmsw は、コード内で動的にデータを生成できます。

  • faker(または @faker-js/faker)と組み合わせることで、毎回異なるデータを返せます。
  • 状態を持ったシナリオテスト(ログイン後の状態など)が可能です。
// msw + @faker-js/faker: 動的データ生成
import { faker } from '@faker-js/faker';

http.get('/api/users', () => {
  return HttpResponse.json({
    name: faker.person.fullName()
  });
});

faker 単体ではデータ生成のみを行います。

  • 本来の faker パッケージは非推奨であり、@faker-js/faker への移行が必須です。
  • 大規模なデータセットを生成する際に有用です。
// @faker-js/faker: 推奨される代替パッケージ
import { faker } from '@faker-js/faker';

const user = {
  id: faker.string.uuid(),
  email: faker.internet.email()
};

⚠️ メンテナンス状況と非推奨パッケージについて

アーキテクチャ選定において、パッケージの将来性は無視できません。

  • faker: 公式に非推奨(Deprecated)となっています。セキュリティ修正や新機能は提供されないため、新規プロジェクトでの使用は避けてください。代わりに @faker-js/faker を使用します。
  • miragejs: 機能は強力ですが、近年メンテナンスの頻度が低下しています。長期プロジェクトでは、より活発に開発されている msw への移行を検討する価値があります。
  • msw: 現在最もアクティブにメンテナンスされており、コミュニティの支持も厚いです。
  • axios-mock-adapter, nock, json-server: 特定のユースケースにおいて依然として有効ですが、それぞれ前述の環境制限や用途の限定性があります。

📊 選定マトリクス:用途別おすすめ

用途推奨パッケージ理由
現代のフロントエンド開発mswブラウザ/Node 両対応、ネットワークレベルインターセプト
Axios 限定のユニットテストaxios-mock-adapter設定が簡単で Axios に特化
Node.js 統合テストnockHTTP ソケットレベルの確実なインターセプト
プロトタイピングjson-serverコードレスで即時 API 構築
データ生成@faker-js/fakerfaker は非推奨のためフォーク版を使用
アプリ内モックサーバーmiragejsORM 機能があるがメンテナンスに注意

💡 結論:現代のスタンダードへ

技術選定の結論として、新規プロジェクトでは msw を第一候補とすることを推奨します。ネットワークレベルでのインターセプトは、アプリケーションコードを汚さず、かつ本番環境に近い挙動を再現できるためです。データ生成には faker ではなく @faker-js/faker を使用し、パッケージの健全性を保つ必要があります。

nock は Node.js 固有のテストに、json-server は初期プロトタイピングに役割を任せ、コアとなる開発・テスト環境は msw で統一するのが — 現代的なフロントエンドアーキテクチャにおける — 最も堅実な選択です。

選び方: axios-mock-adapter vs faker vs json-server vs miragejs vs msw vs nock

  • axios-mock-adapter:

    Axios を既に採用しているプロジェクトで、HTTP リクエストレベルではなく Axios インターセプターレベルでモックを扱いたい場合に選択します。設定が簡単で依存関係も少ないため、ユニットテストで特定の Axios 呼び出しをピンポイントで検証するのに最適です。ただし、Fetch API や他の HTTP クライアントには対応していないため、技術スタックが Axios に固定されている場合に限定されます。

  • faker:

    このパッケージは現在メンテナンスが終了しており、非推奨です。偽データ生成が必要な場合は、コミュニティフォークである @faker-js/faker を使用すべきです。レガシープロジェクトの維持以外で新規採用することは避け、セキュリティリスクやバグ修正の不足に注意してください。

  • json-server:

    バックエンド実装前にフロントエンドのみでプロトタイピングを迅速に行いたい場合に選択します。コードを書かず JSON ファイル一つで REST API を立ち上げられるため、初期開発やデモ作成に極めて効率的です。ただし、複雑なロジックや認証処理には対応できないため、本番環境のモックとしては不向きです。

  • miragejs:

    アプリケーション内部にモックサーバーを構築し、ブラウザ上で動作する統合テストや開発環境として利用したい場合に選択します。ORM 機能を持ち、データの関係性を定義できるため、複雑なデータ構造のモックに適しています。ただし、近年メンテナンス頻度が低下しているため、長期プロジェクトでは代替手段の検討が必要です。

  • msw:

    ブラウザと Node.js 両方の環境で動作する、現代적인フロントエンド開発のデファクトスタンダードです。Service Worker を利用してネットワークレベルでリクエストをインターセプトするため、アプリケーションコードの変更 없이 モックを切り替えられます。アクティブにメンテナンスされており、型安全性や機能面でも最もバランスが取れています。

  • nock:

    Node.js 環境でのバックエンド統合テストや、サーバーサイドの HTTP リクエストモック化に特化して選択します。ソケットレベルで HTTP リクエストをインターセプトするため、精度の高いテストが可能ですが、ブラウザ環境では動作しません。フロントエンド単体のテストというよりは、Node.js を介した E2E テストやバックエンドモックに適しています。

axios-mock-adapter のREADME

axios-mock-adapter

Axios adapter that allows to easily mock requests

Installation

Using npm:

$ npm install axios-mock-adapter --save-dev

It's also available as a UMD build:

axios-mock-adapter works on Node as well as in a browser, it works with axios v0.17.0 and above.

Example

Mocking a GET request

const axios = require("axios");
const AxiosMockAdapter = require("axios-mock-adapter");

// This sets the mock adapter on the default instance
const mock = new AxiosMockAdapter(axios);

// Mock any GET request to /users
// arguments for reply are (status, data, headers)
mock.onGet("/users").reply(200, {
  users: [{ id: 1, name: "John Smith" }],
});

axios.get("/users").then(function (response) {
  console.log(response.data);
});

Mocking a GET request with specific parameters

const axios = require("axios");
const AxiosMockAdapter = require("axios-mock-adapter");

// This sets the mock adapter on the default instance
const mock = new AxiosMockAdapter(axios);

// Mock GET request to /users when param `searchText` is 'John'
// arguments for reply are (status, data, headers)
mock.onGet("/users", { params: { searchText: "John" } }).reply(200, {
  users: [{ id: 1, name: "John Smith" }],
});

axios
  .get("/users", { params: { searchText: "John" } })
  .then(function (response) {
    console.log(response.data);
  });

When using params, you must match all key/value pairs passed to that option.

To add a delay to responses, specify a delay amount (in milliseconds) when instantiating the adapter

// All requests using this instance will have a 2 seconds delay:
const mock = new AxiosMockAdapter(axiosInstance, { delayResponse: 2000 });

You can restore the original adapter (which will remove the mocking behavior)

mock.restore();

You can also reset the registered mock handlers with resetHandlers

mock.resetHandlers();

You can reset both registered mock handlers and history items with reset

mock.reset();

reset is different from restore in that restore removes the mocking from the axios instance completely, whereas reset only removes all mock handlers that were added with onGet, onPost, etc. but leaves the mocking in place.

Mock a low level network error

// Returns a failed promise with Error('Network Error');
mock.onGet("/users").networkError();

// networkErrorOnce can be used to mock a network error only once
mock.onGet("/users").networkErrorOnce();

Mock a network timeout

// Returns a failed promise with Error with code set to 'ECONNABORTED'
mock.onGet("/users").timeout();

// timeoutOnce can be used to mock a timeout only once
mock.onGet("/users").timeoutOnce();

Passing a function to reply

mock.onGet("/users").reply(function (config) {
  // `config` is the axios config and contains things like the url

  // return an array in the form of [status, data, headers]
  return [
    200,
    {
      users: [{ id: 1, name: "John Smith" }],
    },
  ];
});

Passing a function to reply that returns an axios request, essentially mocking a redirect

mock.onPost("/foo").reply(function (config) {
  return axios.get("/bar");
});

Using a regex

mock.onGet(/\/users\/\d+/).reply(function (config) {
  // the actual id can be grabbed from config.url

  return [200, {}];
});

Using variables in regex

const usersUri = "/users";
const url = new RegExp(`${usersUri}/*`);

mock.onGet(url).reply(200, users);

Specify no path to match by verb alone

// Reject all POST requests with HTTP 500
mock.onPost().reply(500);

Chaining is also supported

mock.onGet("/users").reply(200, users).onGet("/posts").reply(200, posts);

.replyOnce() can be used to let the mock only reply once

mock
  .onGet("/users")
  .replyOnce(200, users) // After the first request to /users, this handler is removed
  .onGet("/users")
  .replyOnce(500); // The second request to /users will have status code 500
// Any following request would return a 404 since there are
// no matching handlers left

Mocking any request to a given url

// mocks GET, POST, ... requests to /foo
mock.onAny("/foo").reply(200);

.onAny can be useful when you want to test for a specific order of requests

// Expected order of requests:
const responses = [
  ["GET", "/foo", 200, { foo: "bar" }],
  ["POST", "/bar", 200],
  ["PUT", "/baz", 200],
];

// Match ALL requests
mock.onAny().reply((config) => {
  const [method, url, ...response] = responses.shift();
  if (config.url === url && config.method.toUpperCase() === method)
    return response;
  // Unexpected request, error out
  return [500, {}];
});

Requests that do not map to a mock handler are rejected with a HTTP 404 response. Since handlers are matched in order, a final onAny() can be used to change the default behaviour

// Mock GET requests to /foo, reject all others with HTTP 500
mock.onGet("/foo").reply(200).onAny().reply(500);

Mocking a request with a specific request body/data

mock.onPut("/product", { id: 4, name: "foo" }).reply(204);

Using an asymmetric matcher, for example Jest matchers

mock
  .onPost(
    "/product",
    { id: 1 },
    {
      headers: expect.objectContaining({
        Authorization: expect.stringMatching(/^Basic /),
      })
    }
  )
  .reply(204);

Using a custom asymmetric matcher (any object that has a asymmetricMatch property)

mock
  .onPost("/product", {
    asymmetricMatch: function (actual) {
      return ["computer", "phone"].includes(actual["type"]);
    },
  })
  .reply(204);

.passThrough() forwards the matched request over network

// Mock POST requests to /api with HTTP 201, but forward
// GET requests to server
mock
  .onPost(/^\/api/)
  .reply(201)
  .onGet(/^\/api/)
  .passThrough();

Recall that the order of handlers is significant

// Mock specific requests, but let unmatched ones through
mock
  .onGet("/foo")
  .reply(200)
  .onPut("/bar", { xyz: "abc" })
  .reply(204)
  .onAny()
  .passThrough();

Note that passThrough requests are not subject to delaying by delayResponse.

If you set onNoMatch option to passthrough all requests would be forwarded over network by default

// Mock all requests to /foo with HTTP 200, but forward
// any others requests to server
const mock = new AxiosMockAdapter(axiosInstance, { onNoMatch: "passthrough" });

mock.onAny("/foo").reply(200);

Using onNoMatch option with throwException to throw an exception when a request is made without match any handler. It's helpful to debug your test mocks.

const mock = new AxiosMockAdapter(axiosInstance, { onNoMatch: "throwException" });

mock.onAny("/foo").reply(200);

axios.get("/unexistent-path");

// Exception message on console:
//
// Could not find mock for: 
// {
//   "method": "get",
//   "url": "http://localhost/unexistent-path"
// }

As of 1.7.0, reply function may return a Promise:

mock.onGet("/product").reply(function (config) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      if (Math.random() > 0.1) {
        resolve([200, { id: 4, name: "foo" }]);
      } else {
        // reject() reason will be passed as-is.
        // Use HTTP error status code to simulate server failure.
        resolve([500, { success: false }]);
      }
    }, 1000);
  });
});

Composing from multiple sources with Promises:

const normalAxios = axios.create();
const mockAxios = axios.create();
const mock = new AxiosMockAdapter(mockAxios);

mock
  .onGet("/orders")
  .reply(() =>
    Promise.all([
      normalAxios.get("/api/v1/orders").then((resp) => resp.data),
      normalAxios.get("/api/v2/orders").then((resp) => resp.data),
      { id: "-1", content: "extra row 1" },
      { id: "-2", content: "extra row 2" },
    ]).then((sources) => [
      200,
      sources.reduce((agg, source) => agg.concat(source)),
    ])
  );

History

The history property allows you to enumerate existing axios request objects. The property is an object of verb keys referencing arrays of request objects.

This is useful for testing.

describe("Feature", () => {
  it("requests an endpoint", (done) => {
    const mock = new AxiosMockAdapter(axios);
    mock.onPost("/endpoint").replyOnce(200);

    feature
      .request()
      .then(() => {
        expect(mock.history.post.length).toBe(1);
        expect(mock.history.post[0].data).toBe(JSON.stringify({ foo: "bar" }));
      })
      .then(done)
      .catch(done.fail);
  });
});

You can clear the history with resetHistory

mock.resetHistory();