この記事を読んでできるようになること
- Hoverflyとは何か,どんな機能があるのかを知ることができる.
- HTTP(S)トラフィックの検証にHoverflyの利用を検討できる.
Hoverflyの公式ドキュメントを一通り読んで,ユースケースとしてまとめてみました.
「こんな使い方もあるよ!」「ここ違うんじゃない?」などあれば気軽にコメントください!
Hoverflyとは
HTTP(S)トラフィックのキャプチャと高自由度の制御を提供するOSSのAPIシミュレーションツールです.
Hoverflyは,キャプチャしたトラフィックをSimulations
という名前のschema(論理構造)に基づいて処理し,もっともらしいresponseを返答します.
Simulations
は,JSONファイルでexport/importによる管理が可能です.
具体的にいくつかの機能や特徴をリストアップしてみます.
- 簡単にシミュレーションサーバ(ウェブサーバ,プロキシサーバ)を作成できる.
- ステートフルな挙動も再現できる.
- Hoverflyを介するトラフィック/介さないトラフィックを自由に制御できる.
- request, responseの編集を自由に行えるので,アプリおよびアプリが依存する外部のサービスいずれも未完成の場合も利用できる.
- 特にresponseのbodyは,requestの値や乱数を用いて動的にも編集できる.
- 本番を模してdelayも簡単につけられる.
- ...
機能が豊富,かつユーザが制御できる範囲が非常に広い便利なツールであり,導入もすごく簡単なのですが,ググっても和訳がほとんど無いのでとっつきにくさがあるのかもと感じました.
ロゴがニガテという方もいるかもしれませんが...
Hoverflyのユースケース
APIシミュレーションツールは,トップダウンテストにおいてスタブ(モック)を提供するなどの用途に使用されることがしばしばです.
スタブとなるのはテスト対象アプリから切り出されたサービスなので,ここでは"外部サービス"と呼ぶことにします.
"外部サービス"のモックの作成にあたっては,プロキシサーバの応答をどのように含めるべきか悩ましいということもあると思います.
Hoverflyは,プロキシサーバの応答も自由に含めながら"外部サービス"のモックを作成して,テスト用APIサーバを提供します.
次のHTTP(S)通信を行うテスト対象アプリに対して,モックの作成を考えてみたいと思います.
ユースケース-1: テスト対象アプリが依存する"外部サービス"をモック化する.
本番環境から切り離されたテスト環境に,(プロキシサーバの有無に関わらず)"外部サービス"の応答をモックとして持たせることができます.
既存のアプリを改修して速度を上げたり表示を刷新したりしたいという場合などが該当します.
ユースケース-2: テスト対象アプリが依存する"外部サービス"をモック化する.改修中の"外部サービス"のrequest-response関係をモックに登録する.
ユースケース-1に加えて,任意のrequest-responseをモックに登録することもできます.
"外部サービス"も同時に改修する場合が該当します.
ユースケース-3: 改修で追加されるrequest-response関係のみ"外部サービス"モック化する.既存のrequest-response関係は既存の"外部サービス"にて処理する.
テスト環境を本番環境と分離できない(分離しない)場合が該当します.
テストと並行して個人開発をする場合に利用したいことがあるかもしれません.
Hoverflyが責任を持つ範囲を最小限にすることで,Simulations
の作成も簡易化できます.
ユースケース-4: プロキシサーバをモック化する.
ユースケース-1~3では,プロキシサーバを含む/含まないに関係なく"外部サービス"として応答を持つモックを作成していました.
ユースケース-4は,プロキシサーバのみをモック化してHoverflyに担当してもらう場合が該当します.
具体的には,APIキーの差し替え,認証サーバへの通信などをHoverflyが担うことができます.
ユースケース-5: 通信が想定通りかを監視する.
HTTP(S)通信が想定通りに行われているかを判定するために利用します.
ユースケース-1~4ではHTTP(S)トラフィックのどこかでHoverflyによって処理が行われていましたが,ユースケース-5はトラフィックに対して一切干渉しません.
各ユースケースを実現するHoverflyのモード
Hoverflyでは以下の6つのモードを利用して,ウェブサーバやプロキシサーバとして振舞うことで前節のユースケースを実現することができます.
- Capture mode
- Simulate mode
- Spy mode
- Synthesize mode
- Modify mode
- Diff mode
Capture | Simulate | Spy | Synthesize | Modify | Diff | |
---|---|---|---|---|---|---|
ユースケース-1 | 〇 | 〇 | ||||
ユースケース-2 | 〇 | 〇 | △ | |||
ユースケース-3 | 〇 | 〇 | ||||
ユースケース-4 | △ | 〇 | ||||
ユースケース-5 | △ | 〇 |
Capture mode
Hoverflyをプロキシサーバとして起動し,Clientからのrequestと"外部サービス"からのresponseをキャプチャします.
ここでキャプチャした通信を元にSimulations
を構成してくれます.
ステートフルなresponseも対応しており,利用したい場合は--stateful
オプションをつけてCapture modeに設定します.
この機能によって現在実現されているrequest-response関係については,即座にAPIサーバを構築できることを意味します.
Simulate mode
Hoverflyをプロキシサーバまたはウェブサーバとして起動します.
Hoverflyは,requestを受け取りSimulations
を基づいてresponseを返すテスト用APIサーバとなります.
加えて,ユーザが作成した実行ファイル(Middleware
)でresponseに操作を加えることもできます.
"外部サービス"への通信は一切届きません.
Spy mode
Hoverflyをプロキシサーバとして起動します.
Simulations
によって"外部サービス"で処理するトラフィックとHoverflyで処理するトラフィックを判定する,テスト用APIサーバとなります.
"外部サービス"で処理する場合は,トラフィックに一切干渉しません.
Middleware
でresponseに操作を加えることもできます.
Synthesize mode
Hoverflyをプロキシサーバまたはウェブサーバとして起動します.
Hoverflyは,requestを受け取りresponseを返すテスト用APIサーバとなります.
Simulate modeと異なり,Simulations
に紐づいたresponseを採用することなく,直接Middleware
の内容を返答します.
"外部サービス"への通信は一切届きません.
Modify mode
Hoverflyをプロキシサーバとして起動します.
Hoverflyは,Client-"外部サービス"間でMiddleware
により処理されたrequestとresponseを転送する環境を提供します.
Capture modeと違ってSimulations
の作成は行いません.
Diff mode
Hoverflyをプロキシサーバとして起動します.
Client-"外部サービス"間通信にHoverflyを挟んで通信を監視します.
"外部サービス"から送られてくるresponseと,Hoverflyがrequestから想定したresponseを比較します.
比較結果はAPI(GET /api/v2/diff)を利用することで参照できます.
比較結果は,Hoverflyインスタンスが停止されるか明示的に消去されるまで保持されます.
処理ロジックを定義するSimulations
とマッチング検証
ここではSimulations
の中身と処理について説明します.
Simulations
は,送られてきたrequestを評価するための比較条件,条件に合致したときに返答するresponse,responseを遅らせるlatencyなどの設定をまとめて保持します.
Request Matchers(request-response pairs)
, Global Actions(Delays)
, Meta
で構成されます.
役割は次の通り.
名称 | 役割 |
---|---|
Request Matchers -request |
request検証に用いる条件を定義 |
Request Matchers -response |
request検証条件に紐づいたresponseを定義 |
Global Actions - Delays |
Simulations 内で共通でlatencyを定義 |
Meta |
Simulations の管理情報を記載 |
例として,ステートレスなCapture modeにてhttps://www.google.com
にcurl
した際に生成されたSimulations
を以下に示します.
辞書と配列の組み合わせ構造を持つことで,複数の設定を並列して記載できるようになっていることがわかると思います.
(解説のために//
でコメントを入れていますが,実際のSimulations
には含まれません.)
{
"data": {
"pairs": [ // 複数のrequest-responseペアを登録可能
{
"request": { // pair[0]のrequestを定義
"path": [ // requestの"path"フィールドを検証
{
"matcher": "exact", // 完全な一致
"value": "/"
}
],
"method": [ // requestの"method"フィールドを検証
{
"matcher": "exact", // 完全な一致
"value": "GET"
}
],
"destination": [ // requestの"destination"フィールドを検証
{
"matcher": "exact", // 完全な一致
"value": "www.google.com"
}
],
"scheme": [ // requestの"scheme"フィールドを検証
{
"matcher": "exact", // 完全な一致
"value": "https"
}
],
"body": [ // requestの"body"フィールドを検証
{
"matcher": "exact", // 完全な一致
"value": ""
}
]
},
"response": { // pair[0]のrequestに紐づくresponseを定義
"status": 200,
"body": "\u003c!doctype html\u003e\u003chtml itemscope=...",
"encodedBody": false, // responseのbodyにバイナリデータを設定可能
"headers": {
"Alt-Svc": [
"h3=\":443\"; ma=2592000,..."
],
"Cache-Control": [
"private, max-age=0"
],
"Content-Type": [
"text/html; charset=ISO-8859-1"
],
"Date": [
"Mon, 13 Dec 2021 01:35:02 GMT"
],
"Expires": [
"-1"
],
"Hoverfly": [
"Was-Here"
],
"P3p": [
"CP=\"This is not a P3P policy! See ..."
],
"Server": [
"gws"
],
"Set-Cookie": [
"1P_JAR=2021-12-13-01; expires=Wed, ..."
],
"X-Frame-Options": [
"SAMEORIGIN"
],
"X-Xss-Protection": [
"0"
]
},
"templated": false // templateによる動的response構成の有化/無効
}
}
],
"globalActions": { // このSimulationsを使用するときに模擬するlatencyを設定
"delays": [], // 秒数でlatencyを設定
"delaysLogNormal": [] // 対数正規分布の乱数でlatencyを設定
}
},
"meta": { // このSimulationsを管理するためのメタ情報を記入
"schemaVersion": "v5.1",
"hoverflyVersion": "v1.3.0",
"timeExported": "2021-12-13T10:41:57+09:00"
}
}
1つのSimulations
は複数のRequest Matchersをpairs
配列に持つことで応答を分岐させることができます.
(ステートフルな応答を設定する場合もrequest対responseは常に1対1です.この場合はtransitionsState
, removesState
, requiresState
, sequence
などのキーを利用して管理します.)
トラフィック上で送られてきたrequestのフィールドに対しては,request定義内の各要素でマッチング検証を行うので,ここではこの要素を検証フィールドと呼ぶことにします.
検証フィールドはmatcher
タイプとvalue
で構成されます.
matcher
タイプは以下の3種類があり,それぞれvalue
に対してルールに基づいて一致評価します.
matcher タイプ |
評価方法 |
---|---|
exact |
完全な一致 |
glob |
"*"を利用したあいまい検索による一致 |
regex |
正規表現に対する一致 |
request定義の検証フィールドを適宜間引くことで応答を分岐させることもできます.
Hoverflyのデフォルト動作では,送られてきたrequestに対してSimulations
の全request定義内の全検証フィールドを確認し,検証結果がtrue
となる検証フィールド数が最も多いrequest定義に紐づいたresponseを選択して返答します.
templateを利用すると,送られてきたrequestの内容を元に動的にresponseを構成することもできます.
例えば,リクエストパラメータをresponseに含めて返答してほしい場合に活用できます.
また,Helper Methodsを使って逐次算出される乱数を,メールアドレスやIPアドレスなどの形に整形して含めることもできます.
Simulations
においては次の2種類の方法でlatencyを設定できます.
-
globalActions
を使用して,Simulations
で共通のlatencyを設定する. - 各response定義にそれぞれlatencyを設定する.
また,トラフィック全体については後述するMiddleware
を利用してlatencyを設定することもできます.
実際の運用では,Capture modeで生成したSumulations
をsimulation.json
にexportして頒布したり,自由に編集したsimulation.json
をimportしてSimulate modeやSpy modeで使ったりします.
URLフィルタリングによるHoverflyの利用制御
特定のhostやpathに対してHoverflyを動作させるかどうかの制御は,CLIから設定することができます.
指定には正規表現を含む文字列を使用できます.
またこのフィルタリングは全てのmodeで有効です.
Middleware
によるトラフィック操作
Client-"外部サービス"間,またはClient-Hoverfly間のデータ操作は,Simulations
以外にも任意の言語のコード(Middleware)によって実現できます.
コードとresponseの受け渡しは標準入力/標準出力を利用することで,Simulations
をむやみに変更しないようになっています.
したがって,コードの実行環境が存在することと標準入力/標準出力で受け渡しできる環境であることが利用条件になります.
Python, JavaとHoverflyの連携
Python,Javaで開発している場合は,簡単にHoverflyを利用することができます.
- Python - HoverPy
- Java - Hoverfly Java
request-responseのキャッシュ機能
Hoverflyは,送られてきたrequestに対してマッチング検証を行うことで適切なresponseを選択しています.
しかし,送られてきたrequestに対して毎回検証を行うと,responseの選択と応答に時間がかかってしまいます.
そのため,Hoverflyは一度送られてきたrequestに対しては,選択されたresponseとともにキャッシュに残す機能があります.
ここでrequestヘッダは時間やclientによって変化しやすいので,デフォルトでは含まれません.
Simulations
が編集されたとき,キャッシュはリセットされます.
おわりに
いかがでしたでしょうか.
私自身初めてAPIシミュレーションツールを触ってみたのですが,思っていた以上に応用範囲が広そうだと感じました.
簡単にテスト用APIサーバを立てられる点を利用すれば,大規模開発でスタブとして使うだけでなく,ハッカソンでのモック作成でもきっと活躍してくれることだろうと思いました.
ということで,明日のAdvent Calenderでは『フロントエンド/バックエンドを分離して開発するためにHoverflyを利用してみる』を投稿させていただこうと思います.
【追記 2021/12/19】
公開完了しました!
Windows環境にてHoverflyの活用を試みています.
HTTPSを利用する具体的な方法もこちらに書かせていただきました.
ぜひ皆様のご参考になればと思います.