example-petstore.com

サンプル ドメイン · 実際のサービスではありません · ブラウザーでアクセスするとこのページが表示されます · API リクエストには 410 Gone が返されます

ガイド · テスト

サンプルのアドレスではなく Mock API でテストする

api.example-petstore.com のような架空のアドレスを指すコードも、実際のリクエストを送信します。そのリクエストは、その名前を所有している誰かに届きます。Mock API なら、同じ手軽さをそのリスクなしで得られます。自分のマシン上やテストの中で動作し、すぐに応答し、常に期待どおりのデータを返します。

サンプルのアドレスを使わない理由

  • 実在のドメインのように見えるサンプルのアドレスは、他人が登録している可能性があります。その場合、キーやトークンを含むヘッダーも含めて、すべてのリクエストがその人のサーバーに届きます。
  • api.example.com のように example.com の下で使われていない名前はまったく名前解決されないため、コードは実際の応答をどう処理するかを示す前に、タイムアウトや DNS エラーで失敗します。
  • 公開サーバーに依存するテストは遅く不安定で、そのサーバーが変わると動かなくなります。

Mock API はこの 3 つをすべて解決します。localhost またはテストプロセスの中で待ち受けるので、何もマシンの外に出ません。

どのツールをいつ使うか

ツール向いている用途動作形態
PrismOpenAPI の定義があり、それに従った応答がほしい場合ローカルサーバー(Node.js または Docker)、ポート 4010
WireMock正確に記録した応答、エラーケース、遅延。言語を問いませんローカルサーバー(Java または Docker)、ポート 8080
MSWJavaScript と TypeScript: フロントエンド開発と単体テストブラウザー内、または Node.js のテストプロセス内
json-server1 つの JSON ファイルから動く REST API。プロトタイプ向けローカルサーバー(Node.js)、ポート 3000

Swagger Petstore サンプル API そのものを使う場合は、公式イメージをローカルで実行してください。詳しくは Swagger Petstore をお探しですか?をご覧ください。

Prism: OpenAPI ファイルから

Prism は OpenAPI(または Swagger 2.0)の定義を読み込み、そこにあるすべてのオペレーションに、そのファイルの例やスキーマで応答します。定義に合わないリクエストには分かりやすい検証エラーが返るため、本物の API ができる前にクライアントを確認するのに役立ちます。

# Node.js の場合
npm install -g @stoplight/prism-cli
prism mock openapi.yaml

# Docker の場合
docker run --init --rm -v "$(pwd)":/tmp -p 4010:4010 stoplight/prism:5 mock -h 0.0.0.0 /tmp/openapi.yaml

# その後
curl http://127.0.0.1:4010/pets
curl http://127.0.0.1:4010/pets/1 -H "Prefer: code=404"

Prefer ヘッダーを使うと、エラーコードや名前付きの例など、定義の中から特定の応答を選べます。--dynamic(-d)を付けると、Prism はリクエストのたびにスキーマから新しいデータを生成します。

WireMock: 記録された応答

WireMock は、自分で書いたり記録したりしたスタブファイルから応答します。どの言語でも使え、本物のサービスでは狙って起こすことが難しい遅い応答や障害も再現できます。

# mocks/mappings/pet.json
{
  "request":  { "method": "GET", "url": "/v1/pets/1" },
  "response": {
    "status": 200,
    "headers": { "Content-Type": "application/json" },
    "jsonBody": { "id": 1, "name": "Rex", "status": "available" }
  }
}

# mappings/ を含むフォルダー(大きなボディ用の __files/ も)を指定して起動
docker run -it --rm -p 8080:8080 -v "$(pwd)/mocks":/home/wiremock wiremock/wiremock

curl http://localhost:8080/v1/pets/1

タイムアウトをテストするには応答に "fixedDelayMilliseconds": 3000 を、再試行をテストするには 503 などのステータスを追加します。/__admin の管理 API を使うと、テストからスタブを追加したり、どのリクエストが届いたかを確認したりできます。

MSW と PHP のモック: テストの中で

Mock Service Worker は、アプリケーション自体の中でリクエストを横取りします。ブラウザーでは Service Worker を通じて、Node.js ではテストプロセスの中で動作します。テスト対象のコードは通常のベース URL を呼び出し続け、追加のサーバーは動きません。

// handlers.js
import { http, HttpResponse } from 'msw'

export const handlers = [
  http.get('https://api.example.com/v1/pets/:id', ({ params }) =>
    HttpResponse.json({ id: Number(params.id), name: 'Rex', status: 'available' })),
  http.post('https://api.example.com/v1/pets', () =>
    HttpResponse.json({ error: 'name is required' }, { status: 400 })),
]

// テストの中で(Node.js)
import { setupServer } from 'msw/node'
const server = setupServer(...handlers)
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

onUnhandledRequest: 'error' を指定すると、コードがハンドラーのないアドレスを呼び出した時点でテストが失敗します。そのため、置き忘れたサンプルのアドレスが本番環境ではなくテスト実行の段階で見つかります。ブラウザーでは、npx msw init public/ を一度実行し、msw/browser からワーカーを起動します。

PHP のテストでは、Guzzle の MockHandler がテストプロセスの中で同じ役割を果たします。Symfony には MockHttpClient があります。

// PHP, Guzzle: ネットワークを使わず、順番に応答
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;

$mock = new MockHandler([
    new Response(200, ['Content-Type' => 'application/json'], '{"id": 1, "name": "Rex", "status": "available"}'),
    new Response(400, [], '{"error": "name is required"}'),
]);
$client = new Client(['handler' => HandlerStack::create($mock), 'base_uri' => 'https://api.example.com/v1/']);

// PHP, Symfony
$client = new Symfony\Component\HttpClient\MockHttpClient(
    [new Symfony\Component\HttpClient\Response\MockResponse('{"id": 1, "name": "Rex"}')],
    'https://api.example.com/v1/'
);

ハンドラー内のアドレスは api.example.com です。これは予約済みの名前なので、リクエストがモックをすり抜けても本物のサーバーには決して届きません。

json-server: 手早く REST API を

json-server は、1 つの JSON ファイルを、一覧・詳細・作成・更新・削除のルートを備えた REST API に変えます。変更はファイルに書き戻されるので、プロトタイプやデモに便利です。

# db.json
{
  "pets":   [ { "id": "1", "name": "Rex", "status": "available" } ],
  "orders": []
}

npx json-server db.json

curl http://localhost:3000/pets
curl -X POST http://localhost:3000/orders -H "Content-Type: application/json" -d '{"petId": "1"}'

検証や認証の機能はないため、プロトタイプにとどめてください。契約テストには Prism や WireMock のほうが適しています。

本物の API への切り替え

ベース URL は設定に置き、開発とテストではモックを値にして、本物のアドレスはアプリケーションが実際に動く場所だけで使います。

# .env.development
API_BASE_URL=http://localhost:4010

# .env.test
API_BASE_URL=http://localhost:8080

# 本番: ホスティング環境で設定し、リポジトリには決して入れない
API_BASE_URL=https://api.your-real-service.com

サンプルのアドレスが本番環境に届くのを防ぐチェックも含めた詳しい説明は、API クライアントと SDK の設定をご覧ください。

出典