この記事は、株式会社asken (あすけん) Advent Calendar 2024 の16日目の記事です。
こんにちは、askenの入江です。
今回は、外部APIのモックサーバーとして非常に便利な「WireMock」の使い方をご紹介します。
開発でのよくある課題
現代の開発プロジェクトでは、外部APIや外部システムとの連携が欠かせません。しかし、これらの外部システムを利用する際に、次のような問題が発生することがあります。
- テストケースの多様性: 外部システムの動作やユーザの過去の操作に合わせて応答パターンを再現するのが難しい。
- 外部依存の影響: 外部システムがダウンしていると、開発やテストが進まない。
- 特殊シナリオの再現性: 通信エラーや遅延など、特定のシナリオを意図的に再現するのが困難。
これらの課題を解決する手段のひとつとして、モックサーバを導入することがあげられます。
今回、モックサーバとしてWireMockを使ってみたので、WireMockの導入方法や使い方を紹介します。
モックサーバを知らない人や、モックサーバ導入のために情報収集している人の参考になれば幸いです。
WireMockとは?
WireMockは、HTTPベースのモックサーバーを簡単に構築できるオープンソースツールです。以下のような特徴があります。
特徴 | 説明 |
---|---|
REST APIのモック | 任意のリクエストに対するモックレスポンスを簡単に設定可能。 |
ネットワーク問題の再現 | 遅延やタイムアウトなど、通信エラーをシミュレートできる。 |
柔軟な振る舞い | クエリパラメータやヘッダー値に応じたレスポンスの分岐が可能。 |
状態管理 | ユーザー操作や状態に応じて応答内容を変化させることが可能。 |
WireMockの導入
WireMockの導入方法はいくつかありますが、今回はDockerを使用してスタンドアロンモードで動かす方法を解説します。
1. Docker Composeの設定
以下のようなdocker-compose.yml
ファイルを作成します。
version: '3' services: wire-mock: image: wiremock/wiremock:3.10.0 ports: - "8080:8080" volumes: - ./__files:/home/wiremock/__files - ./mappings:/home/wiremock/mappings
フォルダ構成は次のようにします。
./ ├── __files ├── compose.yml └── mappings
2. 起動と確認
以下のコマンドでWireMockを起動します。
docker-compose up -d
起動後、http://localhost:8080/__admin/swagger-ui/
にアクセスすると、管理用のSwagger UIが表示されます。
WireMockの基本的な使い方
ここでは、簡単なモックAPIを作成する方法を説明します。
1. リクエストとレスポンスの基本設定
以下のようなレスポンスを返すモックを作成します。
{ message: "Hello World!" }
mappings
ディレクトリ配下に次のようなhello.json
ファイルを作成します。
{ "request": { "urlPath": "/hello", "method": "POST" }, "response": { "status": 200, "jsonBody": { "message" : "Hello World!" } , "headers": { "Content-Type": "application/json; charset=utf-8" } } }
ファイル作成後、WireMockを再起動するか、以下のエンドポイントを実行して設定をリロードします。
POST http://localhost:8080/__admin/mappings/reset
http://localhost:8080/hello
を実行してみます。
簡単にモックを作成することができました。
2. レスポンスの外部定義
レスポンスボディが大きくなる場合、__files
ディレクトリに外部定義することが可能です。
__files/hello_file.json
を作成:
{ "message" : "Hello World! from __files" }
mappings/hello.json
を以下のように修正:
{ "request": { "urlPath": "/hello", "method": "GET" }, "response": { "status": 200, "bodyFileName": "hello_file.json", "headers": { "Content-Type": "application/json; charset=utf-8" } } }
応用的な使い方
1. リクエストパラメータによる動的レスポンス
リクエストにクエリパラメータを含めた場合に、動的なレスポンスを返す例を紹介します。
レスポンスの内容を動的に変更するために、WireMockに追加の起動オプション設定が必要になります。下記をcompose.ymlを追加して起動しなおします。
entrypoint: [ "/docker-entrypoint.sh", "--global-response-templating"]
設定例
以下の設定により、/hello?name=xxxx
のようなリクエストに応じて動的なレスポンスを返します。
mappings/dynamic_hello.json
を作成:
{ "request": { "urlPath": "/hello", "method": "GET", "queryParameters": { "name": { "matches": ".*" } } }, "response": { "status": 200, "bodyFileName": "hello_user.json", "headers": { "Content-Type": "application/json; charset=utf-8" } } }
__files/dynamic_hello.json
を作成:
{ "message" : "Hello World! {{request.query.name}}" }
/hello?name=hoge
にアクセスします。
すると以下のようなレスポンスが返却されるようになります。
{ "message" : "Hello World! hoge" }
2. ネットワーク遅延のシミュレーション
レスポンスを遅延させて、通信の遅さをシミュレーションすることもできます。
mappings/hello.json
chunkedDribbleDelayを設定:
{ "request": { "urlPath": "/hello", "method": "GET" }, "response": { "status": 200, "bodyFileName": "hello_file.json", "headers": { "Content-Type": "application/json; charset=utf-8" }, "chunkedDribbleDelay": { "numberOfChunks": 5, "totalDuration": 1000 } } }
この設定は、レスポンスの内容を5つのチャンクに分割し、合計で1000msで送信する設定を示しています。
実際に、ブラウザ開発者ツールでNWの遅さをみてみると1秒間かけて通信が行われていることがわかります。
3. 状態を使ったモックの応答変更
WireMockは「シナリオ」と呼ばれる機能を使って、状態が変化するステートフルなモックを設定することができます。
- シナリオには状態を持たせることができ、その状態に応じてリクエストのレスポンスを柔軟に変更することが可能です。
- 特定のリクエストやレスポンスの実行時に、シナリオの状態を更新することが可能です。
この機能を利用することで、ユーザー操作によって状態が変化し、それ応じた異なるレスポンスを返却するAPI挙動を再現できます。
例えば、下記のようにWEBページ上で同意を行うと、APIのレスポンスが変わるようなケースです。
このように、現実のユースケースに即した高度なモックを構築することが可能です。
さきほどのシーケンスにおいて、状態によりレスポンス内容が変わるAPI、状態を変更するAPIを図示すると以下のようになります。
では、これをもとにしてマッピング定義を作成します。
mappings/statefull.json
に 一連のマッピング定義を作成:
{ "mappings": [ { "scenarioName": "state_example", "requiredScenarioState": "Started", "request": { "urlPath": "/api/status", "method": "GET" }, "response": { "status": 200, "bodyFileName": "status_no.json", "headers": { "Content-Type": "application/json" } } }, { "scenarioName": "state_example", "requiredScenarioState": "AfterAgree", "request": { "urlPath": "/api/status", "method": "GET" }, "response": { "status": 200, "bodyFileName": "status_yes.json", "headers": { "Content-Type": "application/json" } } }, { "scenarioName": "state_example", "request": { "urlPath": "/web/confirm", "method": "GET" }, "response": { "status": 200, "bodyFileName": "confirm.html" } }, { "scenarioName": "state_example", "newScenarioState": "AfterAgree", "request": { "urlPath": "/web/agree", "method": "GET" }, "response": { "status": 200, "bodyFileName": "agree.html" } } ] }
__files/status_no.json
初期状態でモックが返却するレスポンス:
{ "status": "no" }
__files/status_yes.json
状態遷移後にモックが返却するレスポンス:
{ "status": "yes" }
__files/confirm.html
間に挟むHTML:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同意確認ページ</title> </head> <body> <div class="container"> <h1>同意しますか?</h1> <a href="/web/agree">はい</a> </div> </body> </html>
__files/agree.html
状態変更するモックが返却するHTML:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同意確認ページ</title> </head> <body> <div class="container"> <h1>同意しました</h1> </div> </body> </html>
シナリオは、初期状態が「Started」として設定されています。RequiredScenarioState
プロパティに設定された状態に応じてレスポンス内容が変わります。
そして、newScenarioState
プロパティと呼ばれる設定に特定のリクエスト後に状態を変更することが可能です。
たとえば上記の例では、次のように振る舞います
- 状態が「Started」で開始。
/api/status
にアクセスするとrequiredScenarioState
がStartedに設定されている定義のレスポンスを返却。/web/agree
にアクセスすると、newScenarioState
の設定に応じて、シナリオの状態が「AfterAgree」に遷移。/api/status
にアクセスするとrequiredScenarioState
がAfterAgreeに設定されている定義のレスポンスを返却。
このようにシナリオの状態を利用して、ユーザ操作によりレスポンスを動的に切り替えることを実現します。
実際に動かして定義を確認してみます。
最初は/api/status
にアクセスすると下記のレスポンスが返却されます。
{ "status": "no" }
/web/confirm
にアクセスします
そのまま「はい」をクリックします。
/web/agree
にリクエストが送信され、このタイミングでシナリオの状態が「Started」から「AfterAgree」に遷移します。
この段階で/api/status
にアクセスすると下記のレスポンスが返却されるようになります。
{ "status": "yes" }
このような複雑なケースもWireMockで実現することができます。
なお状態をリセットにするには/__admin/scenarios/reset
を使います。
まとめ
WireMockのよくある使いかたをまとめてみました。WireMockを活用することで、以下のような開発課題を解決できます。
- 外部依存を排除: 開発環境を外部システムの状況に依存させない。
- テストケースの柔軟性向上: 様々なシナリオを簡単に再現可能。
- チームの効率化: 特殊な条件下でのテストを効率的に行える。
この記事が、開発現場でのモック利用の参考になれば幸いです。
参考
WireMock公式サイト : https://wiremock.org/
積極採用中です
askenではエンジニアを絶賛募集中です。
まずはカジュアルにお話しできればと思いますので、ぜひお気軽にご連絡ください!