あなたのコードは、自分で制御できないサービスに依存しています。決済ゲートウェイ、パートナーAPI、レガシーメインフレーム、別チームのマイクロサービスなどです。これらはメンテナンスで停止したり、レート制限されたり、呼び出しごとに課金されたり、不安定なデータを返したりします。その結果、開発が止まり、CIがあなたのコードとは無関係な理由で失敗します。
サービス仮想化は、この問題を解決するための実装パターンです。実際の依存サービスを「本物のように振る舞う代替サービス」に置き換え、開発環境やCIからその仮想サービスを呼び出します。この記事では、サービス仮想化の考え方、モック/スタブとの違い、導入すべきケース、WireMock、Hoverfly、Mountebank、エンタープライズ向け製品、そしてAPI契約ベースの仮想化に使えるApidogのモックサーバーを実装目線で整理します。
サービス仮想化とは
サービス仮想化とは、アプリケーションが依存している外部サービスの振る舞いをシミュレートし、実サービスの代わりにそのシミュレーションへ接続させる手法です。
対象は、たとえば次のようなサービスです。
- 決済API
- 配送会社API
- 認証・認可サービス
- パートナー企業のAPI
- 社内の別チームが管理するマイクロサービス
- レガシーシステムやメインフレーム
重要なのは、単に固定JSONを返すだけではない点です。実用的な仮想サービスは、次のような振る舞いを再現します。
- HTTPメソッド、パス、クエリ、ヘッダー、ボディでリクエストをマッチングする
- 入力に応じて異なるレスポンスを返す
- 呼び出し間で状態を保持する
- レスポンス遅延を入れる
- 4xx/5xx、タイムアウト、不正なレスポンスを返す
- 実サービスと同じプロトコルで通信する
依存サービスがHTTP APIであれば、「API仮想化」や「APIシミュレーター」もほぼ同じ文脈で使われます。
サービス仮想化 vs モック vs スタブ
この3つは混同されがちです。実装時は、次のように分けると判断しやすくなります。
| 用語 | 実行場所 | 主な用途 | 例 |
|---|---|---|---|
| スタブ | テストコード内 | 固定レスポンスを返す | 常に同じユーザーJSONを返す |
| モック | テストコード内 | 呼び出し内容を検証する |
sendEmail() が1回呼ばれたか検証する |
| サービス仮想化 | ネットワーク上 | 実サービスの代替として動く |
http://localhost:8080 に仮想APIを立てる |
スタブ
スタブは、ハードコードされたレスポンスを返します。
たとえば、テストが GET /user/42 を呼び出したときに、常に次を返すようなものです。
{
"id": 42,
"name": "Ada Lovelace"
}
リクエストの中身を細かく見ず、固定値を返すだけならスタブです。
モック
モックは、レスポンスを返すだけでなく「どのように呼ばれたか」を検証します。
たとえば、単体テストで次を確認します。
- メソッドが呼ばれたか
- 引数が正しいか
- 呼び出し回数が正しいか
- 呼び出し順序が正しいか
APIのスタブとモックの違いは、APIスタブ vs APIモックでも詳しく解説されています。
サービス仮想化
サービス仮想化は、アプリケーションが接続するネットワークサービスそのものを立ち上げます。
アプリケーション側は、次のようにベースURLを差し替えるだけです。
# 本番
PAYMENT_API_BASE_URL=https://payment.example.com
# テスト
PAYMENT_API_BASE_URL=http://localhost:8080
アプリケーションは実サービスと同じようにHTTPリクエストを送り、仮想サービスからレスポンスを受け取ります。
HTTP仮想サービスを「モックAPI」や「モックサーバー」と呼ぶこともあります。実務上は問題ありません。重要なのは、プロセス内のオブジェクトを置き換えているのか、ネットワーク上の依存サービスを置き換えているのかです。
モックの基本は、APIモック:知っておくべきことすべても参考になります。
チームがサービス仮想化を使う理由
サービス仮想化が役立つのは、主に次のケースです。
1. 依存サービスがまだ完成していない
フロントエンドとバックエンドを並行開発している場合、API実装が完了するまでフロントエンドが待つのは非効率です。
API契約から仮想サービスを作れば、バックエンド実装前でもフロントエンドは次の作業を進められます。
- 画面実装
- API接続処理
- エラーハンドリング
- E2Eテスト
- UI状態の確認
2. 依存サービスが高価またはレート制限されている
サードパーティAPIは、次の制約を持つことがあります。
- 呼び出しごとに課金される
- サンドボックスにレート制限がある
- テストデータの作成に手間がかかる
- CIから大量実行できない
仮想サービスを使えば、CIで何度でも無料かつ高速にテストできます。
3. 依存サービスの障害を再現しにくい
実サービスで次の状態を意図的に起こすのは難しいです。
- 500エラー
- タイムアウト
- 遅いレスポンス
- 不正なJSON
- 必須フィールド欠落
- 期限切れトークン
- 一時的な接続失敗
仮想サービスなら、これらをテストケースとして明示的に作れます。
{
"request": {
"method": "POST",
"url": "/payments"
},
"response": {
"status": 500,
"jsonBody": {
"error": "payment_provider_unavailable"
}
}
}
4. CIを決定論的にできる
実サービスのデータや状態は変わります。仮想サービスは固定された状態で起動できるため、テスト結果が安定します。
ビルドが失敗したときに、「コードが悪いのか、外部APIが不安定なのか」を切り分けやすくなります。
サービス仮想化の仕組み
サービス仮想化ツールは、多くの場合、次のどちらかの方式で動きます。
記録と再生
実サービスへの通信を一度プロキシ経由で流し、リクエスト/レスポンスを記録します。その後、同じリクエストに対して記録済みレスポンスを返します。
向いているケース:
- 実サービスがすでに存在する
- レスポンスを手書きしたくない
- 既存の通信を短時間で仮想化したい
代表例:
- Hoverflyのキャプチャモード
- WireMockの記録モード
仕様駆動型
OpenAPIなどのAPI契約や、手動で定義したルールから仮想サービスを作ります。
向いているケース:
- 実サービスがまだ存在しない
- API契約を先に決めている
- レスポンスの内容を細かく制御したい
- フロントエンド開発を先行させたい
代表例:
- Apidogのモックサーバー
- WireMockのスタブ定義
- Mountebankのインポスター定義
基本はリクエストマッチング
多くのツールは、受信リクエストを次の条件で判定します。
- HTTPメソッド
- パス
- クエリパラメータ
- ヘッダー
- リクエストボディ
- Cookie
- 正規表現
- JSONPath
一致したルールに紐づくレスポンスを返します。
たとえば、次のようなルールです。
GET /users/42
→ 200 OK + ユーザー情報
GET /users/999
→ 404 Not Found
POST /payments amount=0
→ 400 Bad Request
POST /payments amount>0
→ 201 Created
ツール
ここからは、実装で使いやすい代表的なツールを見ていきます。機能やバージョンは変わるため、導入前には各公式ドキュメントを確認してください。
WireMock
WireMockは、HTTP向けのオープンソースAPIモック/サービス仮想化ツールです。
主な特徴:
- スタンドアロンJARとして実行できる
- JVMテストに組み込める
- Dockerで実行できる
- JSONでスタブを定義できる
- 記録と再生に対応
- レスポンステンプレートに対応
- ステートフルなシナリオに対応
- 遅延や障害を注入できる
起動する
スタンドアロンJARを使う場合:
java -jar wiremock-standalone-3.13.2.jar --port 8080
Dockerで起動する場合は、プロジェクトの構成に合わせてコンテナ化できます。
スタブを登録する
管理APIに対して、スタブ定義をPOSTします。
curl -X POST http://localhost:8080/__admin/mappings \
-H "Content-Type: application/json" \
-d '{
"request": {
"method": "GET",
"url": "/user/42"
},
"response": {
"status": 200,
"headers": { "Content-Type": "application/json" },
"jsonBody": { "id": 42, "name": "Ada Lovelace" }
}
}'
確認します。
curl http://localhost:8080/user/42
レスポンス:
{
"id": 42,
"name": "Ada Lovelace"
}
エラーケースを追加する
curl -X POST http://localhost:8080/__admin/mappings \
-H "Content-Type: application/json" \
-d '{
"request": {
"method": "GET",
"url": "/user/999"
},
"response": {
"status": 404,
"headers": { "Content-Type": "application/json" },
"jsonBody": { "error": "user_not_found" }
}
}'
WireMockは、HTTP依存サービスを細かく制御したい場合に強力です。JVMアプリケーションやコンテナベースのCIとも相性がよいです。マネージド環境が必要な場合は、商用のWireMock Cloudもあります。
Hoverfly
Hoverflyは、Goで書かれた軽量なオープンソースのサービス仮想化ツールです。プロキシとして動作し、外部サービスへの通信をキャプチャして再生できます。
主なモード:
- capture
- simulate
- spy
- modify
- synthesize
- diff
キャプチャして再生する
hoverctl を使った基本フローです。
hoverctl start
hoverctl mode capture
curl --proxy http://localhost:8500 https://api.example.com/user/42
hoverctl export simulation.json
hoverctl mode simulate
Hoverflyでは、管理APIはデフォルトで 8888、プロキシは 8500 で動作します。
この後、同じリクエストをプロキシ経由で送ると、実サービスではなくキャプチャ済みのレスポンスが返ります。
curl --proxy http://localhost:8500 https://api.example.com/user/42
Hoverflyは、既存の実サービスの通信を素早く仮想化したいときに便利です。レスポンスをすべて手書きするより、実トラフィックから作るほうが早いケースに向いています。ホスト型のHoverfly Cloudもあります。
Mountebank
Mountebankは、「インポスター(imposter)」という単位で仮想サービスを定義するオープンソースツールです。
インポスターは、特定のポートで待ち受け、特定のプロトコルで応答する仮想サービスです。
対応プロトコルには、次のようなものがあります。
- HTTP
- HTTPS
- TCP
- SMTP
REST APIだけでなく、TCPやSMTPも扱いたい場合に有用です。
インストールして起動する
npm install -g mountebank
mb
デフォルトでは、コントロールプレーンが 2525 で起動します。
インポスターを作成する
curl -X POST http://localhost:2525/imposters \
-H "Content-Type: application/json" \
-d '{
"port": 4545,
"protocol": "http",
"stubs": [{
"predicates": [{ "equals": { "method": "GET", "path": "/user/42" } }],
"responses": [{ "is": { "statusCode": 200, "body": "{\"id\":42,\"name\":\"Ada Lovelace\"}" } }]
}]
}'
確認します。
curl http://localhost:4545/user/42
レスポンス:
{"id":42,"name":"Ada Lovelace"}
Mountebankでは、predicates がリクエストの一致条件、responses が返す内容です。HTTP以外のプロトコルを扱える点が、他の軽量ツールとの差別化ポイントです。
Parasoft VirtualizeおよびBroadcom Service Virtualization
大規模エンタープライズ向けには、Parasoft VirtualizeやBroadcom Service Virtualization(DevTestスイートの一部)があります。
これらは、次のような要件を持つ組織向けです。
- HTTP以外の多くのプロトコルを仮想化したい
- メインフレーム、MQ、固定長メッセージなどを扱う
- 複数チーム・複数環境で仮想サービスを一元管理したい
- 外部データソースと接続したデータ駆動型の仮想サービスが必要
- 組織全体でガバナンスを効かせたい
一方で、導入コストやセットアップの負荷は大きくなります。
少数のHTTP APIを仮想化したいだけであれば、WireMock、Hoverfly、Mountebank、またはAPIプラットフォームのモックサーバーのほうが始めやすいです。
Apidog: API契約のための軽量なサービス仮想化
仮想化したい依存サービスがHTTP APIであり、API契約から開発を進めたい場合、Apidogのモックサーバーが使えます。
Apidogでは、APIを設計またはインポートすると、モックサーバーを利用できます。バックエンド実装がまだなくても、フロントエンドやテストコードはスキーマに基づくエンドポイントへ接続できます。
Apidogが向いているケース
- OpenAPIなどのAPI定義からモックを作りたい
- フロントエンド開発をバックエンド実装前に進めたい
- API契約に沿ったレスポンスを返したい
- 手動で大量のJSONレスポンスを書きたくない
- ローカル開発とCIでモックサーバーを使いたい
Apidogのモックは、単なる固定スタブではなく、スキーマを認識した動的レスポンスを生成できます。
たとえば、スキーマに次のようなフィールドがある場合:
{
"type": "object",
"required": ["id", "email", "city"],
"properties": {
"id": { "type": "integer" },
"email": { "type": "string", "format": "email" },
"city": { "type": "string" }
}
}
Apidogは型、フォーマット、必須フィールド、列挙型などの制約を考慮してレスポンスを生成します。email というフィールドにはメールアドレスらしい値、city というフィールドには都市名らしい値を返せます。
より細かい制御が必要な場合は、固定値やFaker.jsベースの動的値を使い分けられます。
リクエスト条件ごとにレスポンスを変える
サービス仮想化で重要なのは、入力に応じて出力を変えることです。
たとえば、次のような条件を作れます。
GET /users/42
→ 200 OK
GET /users/999
→ 404 Not Found
POST /orders with invalid body
→ 400 Bad Request
このように、リクエストパラメータに基づいて異なるレスポンスを返すカスタムモックルールを作れます。これは固定スタブではなく、仮想サービスとしてのリクエストマッチングに近い使い方です。
詳しい手順は、1分でAPIをモックする方法を参照してください。
Apidogが適していること・適していないこと
Apidogが適している用途:
- HTTP/REST APIの契約ベース仮想化
- API設計とモックの連携
- フロントエンド/バックエンド並行開発
- スキーマに基づく動的レスポンス生成
- APIドキュメント、テスト、モックの一元管理
Apidogが適していない用途:
- 任意のレガシープロトコルのプロキシ記録・再生
- TCP、SMTP、MQなどHTTP以外の広範な仮想化
- ロードジェネレーターとしての利用
HTTP APIが中心ならApidogは軽量に始めやすい選択肢です。より広い比較は、オンラインAPIモックツールの比較とAPIモックツールベスト10も参考になります。
始め方
サービス仮想化は、ツールから選ぶのではなく、依存サービスの性質から選ぶのが安全です。
1. プロトコルを確認する
まず、仮想化したい依存サービスのプロトコルを確認します。
HTTP/REST → WireMock、Hoverfly、Apidog、Mountebank
TCP/SMTP → Mountebank
大規模レガシー/エンタープライズ → Parasoft、Broadcom
通常のWeb APIであれば、HTTP向けツールまたはAPIプラットフォームで十分です。
2. 記録型か仕様駆動型かを決める
実サービスがすでにあり、通信を流せる場合:
Hoverfly
WireMockの記録モード
実サービスがまだない、またはAPI契約から作りたい場合:
Apidog
WireMockスタブ
Mountebankインポスター
3. アプリケーションの接続先を切り替える
アプリケーションでは、依存サービスのURLを環境変数で切り替えられるようにします。
USER_API_BASE_URL=http://localhost:8080
PAYMENT_API_BASE_URL=http://localhost:4545
コード側では、ハードコードせず設定値から読むようにします。
const userApiBaseUrl = process.env.USER_API_BASE_URL;
const response = await fetch(`${userApiBaseUrl}/user/42`);
const user = await response.json();
これにより、ローカル・CI・ステージング・本番で接続先を安全に切り替えられます。
4. 正常系だけでなく障害系を作る
仮想サービスの価値は、実サービスで再現しにくい障害系をテストできることです。
最低限、次を用意します。
- 400 Bad Request
- 401 Unauthorized
- 403 Forbidden
- 404 Not Found
- 409 Conflict
- 429 Too Many Requests
- 500 Internal Server Error
- タイムアウト
- 遅いレスポンス
- 不正なJSON
- 必須フィールド欠落
例:
{
"request": {
"method": "GET",
"url": "/inventory/sku-001"
},
"response": {
"status": 429,
"headers": {
"Retry-After": "30",
"Content-Type": "application/json"
},
"jsonBody": {
"error": "rate_limited"
}
}
}
5. CIで起動する
ローカルだけで使う仮想サービスは、開発者の助けにはなりますが、ビルドの安定化には不十分です。
CIで次の流れを作ります。
1. 仮想サービスを起動
2. アプリケーションの環境変数を仮想サービスURLに設定
3. テストを実行
4. 仮想サービスを停止
Dockerを使う場合は、アプリと仮想サービスを同じネットワークに置くと扱いやすくなります。
ヘッドレスで動くモックが必要な場合は、ヘッドレスAPIモックツールも参考になります。
仮想依存サービスとライブ依存サービスの使い分けは、モックサーバー vs リアルサーバーでも解説されています。
よくある質問
サービス仮想化はモックと同じですか?
厳密には違います。
モックは通常、単体テストでプロセス内のオブジェクトを置き換え、呼び出し内容を検証します。サービス仮想化は、アプリケーションが実際のプロトコルで接続するネットワークサービスを立ち上げます。
ただし、HTTPの仮想サービスを「モックAPI」や「モックサーバー」と呼ぶことはよくあります。
実際のテスト環境の代わりに仮想サービスを使うべきなのはいつですか?
次の条件に当てはまる場合です。
- 実サービスがまだない
- 実サービスが不安定
- レート制限がある
- 呼び出しに課金される
- 障害系を再現しにくい
- CIを安定させたい
ただし、最終的な統合確認のために、実サービスに対する小規模なテストセットは残すべきです。
仮想サービスはステートフルにできますか?
はい。WireMockやMountebankのようなツールは、以前のリクエストに応じてレスポンスを変えるステートフルなシナリオをサポートしています。
例:
GET /cart
→ 空のカート
POST /cart/items
→ 商品を追加
GET /cart
→ 商品入りのカート
単発のAPI呼び出しだけでなく、ワークフロー全体をテストしたい場合に重要です。
有料のエンタープライズツールは必要ですか?
通常のHTTP APIだけなら、多くの場合は不要です。
WireMock、Hoverfly、Mountebank、またはAPIプラットフォームのモックサーバーで十分対応できます。
有料のエンタープライズツールが必要になるのは、次のような場合です。
- 多数の非HTTPプロトコルを扱う
- メインフレームやMQなどを大規模に仮想化する
- 組織全体で仮想サービスを一元管理する
- 複雑なデータソース連携が必要
HTTP APIを仕様から仮想化する最速の方法は何ですか?
API定義を、スキーマからモックを生成できるツールにインポートする方法です。
Apidogでは、APIを設計またはOpenAPI仕様をインポートすると、スキーマに基づくモックサーバーを利用できます。まずは自動生成されたレスポンスで開発を進め、必要に応じて特定のリクエスト/レスポンス条件をカスタムルールとして追加します。
Top comments (0)