はじめに
もしもしもしもし〜1、nikkieです。
HTTPサーバをモックできるWireMockを最近触りました。
この技術、「非常に簡単に間違えずに使える!」と感動したので、今回アウトプットします。
目次
- はじめに
- 目次
- WireMockとは
- WireMockをJSONファイルで設定してDockerで動かす
- GET /chinanago で「さかなー」と返すように設定する
- POST /takina にchinanagoのJSONが送られたら「さかなー」と返すように設定する
- POST /takina の「さかなー」のレスポンスをJSONファイルを使って設定する
- まとめ:WireMockをJSONファイルで設定する
- 終わりに
WireMockとは
HTTPサーバをモックできます2。
2つの用途で使えるそうです3。
REST APIやJavaのAPIも提供されていますが、今回はJSONファイルからスタブの設定4を触りました。
これが非常に簡単だったんです!
WireMockをJSONファイルで設定してDockerで動かす
Configuring and running WireMock in Docker | WireMock
ドキュメントの「Mounting stub mapping files」を参照します。
- 作業ディレクトリに、JSONを配置するための2つのディレクトリを用意する
mappings
__files
mappings
と__files
があるディレクトリをDockerコンテナの/home/wiremock
にマウントする- 👉
-v $PWD:/home/wiremock
- 👉
新しい概念がたくさん出てきましたが、1つ1つ見ていきましょう。
モックするサーバは『リコリス・リコイル』の「さかなー」「ちんあなごー」を模します5。
GET /chinanago で「さかなー」と返すように設定する
mappings
にJSONファイルtakina.json
を作ります。
{ "request": { "urlPath": "/chinanago", "method": "GET" }, "response": { "status": 200, "body": "さかなー🐟" } }
パス/chinanago
6にGETメソッドでリクエストされたときのスタブを設定しています(request
の部分)。
レスポンスは200 OKを返し、ボディは「さかなー🐟」と設定しました(response
の部分)。
設定値について詳しくは Matching and filtering HTTP requests in WireMock | WireMock (1) に記載されています。
mappings/takina.json
を置いた状態でWireMockのDockerイメージをrunしましょう!7
docker run -it --rm \ -p 8080:8080 \ --name chisataki-mock \ -v $PWD:/home/wiremock \ wiremock/wiremock:2.35.0
$ curl http://localhost:8080/chinanago さかなー🐟
さかなー🐟🐟🐟🐟!!!!
POST /takina にchinanagoのJSONが送られたら「さかなー」と返すように設定する
次はPOSTリクエストをモックしてみましょう。
mappings/takina.json
のrequest
を変えていきます。
⚒変える箇所
urlPath
(完全に好みで/takina
にcurl
したかったので変えました)method
:POSTリクエストを指定headers
を追加- Content-Typeが
application/json
の場合のスタブです9
- Content-Typeが
bodyPatterns
を追加
{ "request": { - "urlPath": "/chinanago", - "method": "GET" + "urlPath": "/takina", + "method": "POST", + "headers": { + "Content-Type": { + "equalTo": "application/json" + } + }, + "bodyPatterns": [ + { + "equalToJson": { + "lines": "chinanago" + } + } + ] }, "response": { "status": 200, "body": "さかなー🐟" } }
設定を変えたのでWireMockのイメージは一度止めて、再度docker run
(コマンドはこれまでと同じです)。
別のシェルでcurl
します。
$ curl http://localhost:8080/takina -H 'Content-Type: application/json' -d '{"lines": "chinanago"}' さかなー🐟
さかなー🐟🐟🐟🐟🐟🐟🐟🐟!!!!!!!!
.request.bodyPatterns
でequalToJson
をJSONで指定しましたが、ドキュメント(1)によると文字列リテラルとしても指定できます(「JSON with string literal」でページ内を検索すると見つかります)
"bodyPatterns": [ { "equalToJson": "{\"lines\": \"chinanago\"}" } ]
POST /takina の「さかなー」のレスポンスをJSONファイルを使って設定する
Mock API Response Templating | WireMock (2)
mappings/takina.json
の.response.body
はJSON形式の文字列リテラルも指定できます。
{ "request": { "urlPath": "/takina", "method": "POST", "headers": { "Content-Type": { "equalTo": "application/json" } }, "bodyPatterns": [ { "equalToJson": "{\"lines\": \"chinanago\"}" } ] }, "response": { "status": 200, - "body": "さかなー🐟" + "body": "{\"lines\": \"さかなー🐟\"}" } }
$ curl -s http://localhost:8080/takina -H 'Content-Type: application/json' -d '{"lines": "chinanago"}' | jq . { "lines": "さかなー🐟" }
JSONの、さかなー🐟🐟🐟🐟!!!!
ドキュメント(2)によると、response
のbody
は代わりにbodyFileName
を使って__files
下に配置したJSONファイルの内容をレスポンスとして返せます(「Templated body file」)10。
__files/takina.json
を置きましょう。
{ "lines": "さかなー🐟" }
mappings/takina.json
からこのファイルを使うように変更します。
bodyFileName
には__files
以下の相対パスを書くようです。
{ "request": { "urlPath": "/takina", "method": "POST", "headers": { "Content-Type": { "equalTo": "application/json" } }, "bodyPatterns": [ { "equalToJson": "{\"lines\": \"chinanago\"}" } ] }, "response": { "status": 200, - "body": "{\"lines\": \"さかなー🐟\"}" + "bodyFileName": "takina.json" } }
$ curl -s http://localhost:8080/takina -H 'Content-Type: application/json' -d '{"lines": "chinanago"}' | jq . { "lines": "さかなー🐟" }
__files
を使って設定したJSONの、さかなー🐟🐟🐟🐟🐟🐟🐟🐟!!!!!!!!
WireMockのDockerイメージの挙動は以下のようです。
-v
でマウントしているので、ホスト側のファイルを編集すると、コンテナ内のファイルの内容も変わります
mappings
側は起動時にメモリに読み込むようでdocker run
中にファイルの内容が変わっても再度docker run
するまで反映されない__files
はリクエストが来るたびに参照される(メモリに読み込まれていない)ようで、docker run
したままでも返されるレスポンスが変わる
コンテナを止めずに__files/takina.json
を変えてみます。
{ - "lines": "さかなー🐟" + "lines": "さかなー🐟🐟🐟🐟" }
$ curl -s http://localhost:8080/takina -H 'Content-Type: application/json' -d '{"lines": "chinanago"}' | jq . { "lines": "さかなー🐟🐟🐟🐟" }
まとめ:WireMockをJSONファイルで設定する
WireMockを設定するJSONファイルの配置
docker run
では「mappings
と__files
があるディレクトリ」を/home/wiremock
にマウントする。
この記事ではJSONファイルは1つずつしか置いていませんが、複数置くこともできます。
終わりに
「JSONファイルでのWireMockの設定が簡単!」という感動からこの記事にアウトプットしました。
思うに「正しい使い方を簡単に、誤った使い方を困難に」の好例だと思います。
ルールに沿ってJSONファイルを置いてdocker run
するだけでモックサーバが立ち上がる、簡単!
JSONの書き方を間違えたときはdocker run
で落ちてパースに失敗したエラーメッセージが表示されます。
jq
コマンド素振り11など、読み解くために必要な知識があったというバイアスがかかっているかもしれませんが、間違えても正しい道に戻りやすかったです。
また、WireMockには「80/20ルール」12も該当するように思われます。
Gitなどと同様、WireMockも20%を知ることで、日常的なニーズの80%はまかなえそうです。
今回はJSONファイルを使いましたが、REST APIからの設定に使える(?)Pythonライブラリもあるみたいでした。
素振り材料ですね。
GitHub - wiremock/python-wiremock: Python WireMock Admin API Client
今回のサンプルコードはこちらです(たきなをGET、千束をPOSTで設定しました)
-
リコリス・リコイル6話で千束が言ってました。
↩第6話放送後もたくさんお知らせさせていただいたので、モーメントまとめております⚡️
— TVアニメ『リコリス・リコイル』公式 (@lycoris_recoil) 2022年8月6日
お時間ある時にぜひチェックしてください🙇♂️https://t.co/dWiXa156IO - ドキュメントより「WireMock is an HTTP mock server.」↩
- ドキュメントより「At its core it is web server that can be primed to serve canned responses to particular requests (stubbing) and that captures incoming requests so that they can be checked later (verification).」↩
- ドキュメントより「Additionally, stubs can be configured via JSON files.」↩
- 例に使った理由は、好みです!↩
-
takina.json
の.request.urlPath
ですが、ドキュメント(1)の「URL matching」を参照すると他にもやり方があります。正規表現も使えるそうです↩ -
この時点では
__files
ディレクトリは使っていませんが、__files
ディレクトリがない場合はdocker run -v
のマウントにより__files
ディレクトリができました(権限はrootではなく、ホスト側の現在のユーザでした)↩ -
docker run
に-d
を指定していないので、WireMockの出力が表示された状態です。-d
を指定して同じシェルでcurl
してもいいと思います!↩ -
Content-Type
のようなHTTPヘッダー名は「大文字小文字を区別しない」んですよね(contenT-typE
などのように変えてお試しください)。ヘッダーの値については、大文字小文字を区別しないでマッチするように指定する方法("caseInsensitive": true
)がドキュメント(1)の「Case-insensitive equality」に載っています↩ -
この記事の設定例だとあまり嬉しさを感じないかもしれませんが、「Templated body file」には
"bodyFileName": "files/{{request.pathSegments.[1]}}"
とリクエストに応じたファイルを返す設定例があります↩ - JSON Linesをjqコマンドで扱う方法、『jqハンドブック』で完全に理解した! - nikkie-ftnextの日記↩
- 『Clean Agile』にあります。過去に言及したエントリはこちら:はてなスター、好きです!〜引用スターを知ってますます好きに〜 - nikkie-ftnextの日記↩