12
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

NRI OpenStandiaAdvent Calendar 2021

Day 18

HTTP(S)トラフィック検証用のAPIサーバを即座に提供するOSS - Hoverflyの紹介

Last updated at Posted at 2021-12-17

この記事を読んでできるようになること

  • Hoverflyとは何か,どんな機能があるのかを知ることができる.
  • HTTP(S)トラフィックの検証にHoverflyの利用を検討できる.

Hoverflyの公式ドキュメントを一通り読んで,ユースケースとしてまとめてみました.
「こんな使い方もあるよ!」「ここ違うんじゃない?」などあれば気軽にコメントください!

Hoverflyとは

logo-large.png

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)通信を行うテスト対象アプリに対して,モックの作成を考えてみたいと思います.

スライド8.PNG

ユースケース-1: テスト対象アプリが依存する"外部サービス"をモック化する.

スライド9.PNG

本番環境から切り離されたテスト環境に,(プロキシサーバの有無に関わらず)"外部サービス"の応答をモックとして持たせることができます.
既存のアプリを改修して速度を上げたり表示を刷新したりしたいという場合などが該当します.

ユースケース-2: テスト対象アプリが依存する"外部サービス"をモック化する.改修中の"外部サービス"のrequest-response関係をモックに登録する.

スライド10.PNG

ユースケース-1に加えて,任意のrequest-responseをモックに登録することもできます.
"外部サービス"も同時に改修する場合が該当します.

ユースケース-3: 改修で追加されるrequest-response関係のみ"外部サービス"モック化する.既存のrequest-response関係は既存の"外部サービス"にて処理する.

スライド11.PNG

テスト環境を本番環境と分離できない(分離しない)場合が該当します.
テストと並行して個人開発をする場合に利用したいことがあるかもしれません.
Hoverflyが責任を持つ範囲を最小限にすることで,Simulationsの作成も簡易化できます.

ユースケース-4: プロキシサーバをモック化する.

スライド12.PNG

ユースケース-1~3では,プロキシサーバを含む/含まないに関係なく"外部サービス"として応答を持つモックを作成していました.
ユースケース-4は,プロキシサーバのみをモック化してHoverflyに担当してもらう場合が該当します.
具体的には,APIキーの差し替え,認証サーバへの通信などをHoverflyが担うことができます.

ユースケース-5: 通信が想定通りかを監視する.

スライド13.PNG

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

スライド0.PNG

Hoverflyをプロキシサーバとして起動し,Clientからのrequestと"外部サービス"からのresponseをキャプチャします.
ここでキャプチャした通信を元にSimulationsを構成してくれます.
ステートフルなresponseも対応しており,利用したい場合は--statefulオプションをつけてCapture modeに設定します.
この機能によって現在実現されているrequest-response関係については,即座にAPIサーバを構築できることを意味します.

Simulate mode

スライド1.PNG

Hoverflyをプロキシサーバまたはウェブサーバとして起動します.
Hoverflyは,requestを受け取りSimulationsを基づいてresponseを返すテスト用APIサーバとなります.
加えて,ユーザが作成した実行ファイル(Middleware)でresponseに操作を加えることもできます.
"外部サービス"への通信は一切届きません.

Spy mode

スライド2.PNG

Hoverflyをプロキシサーバとして起動します.
Simulationsによって"外部サービス"で処理するトラフィックとHoverflyで処理するトラフィックを判定する,テスト用APIサーバとなります.
"外部サービス"で処理する場合は,トラフィックに一切干渉しません.
Middlewareでresponseに操作を加えることもできます.

Synthesize mode

スライド3.PNG

Hoverflyをプロキシサーバまたはウェブサーバとして起動します.
Hoverflyは,requestを受け取りresponseを返すテスト用APIサーバとなります.
Simulate modeと異なり,Simulationsに紐づいたresponseを採用することなく,直接Middlewareの内容を返答します.
"外部サービス"への通信は一切届きません.

Modify mode

スライド4.PNG

Hoverflyをプロキシサーバとして起動します.
Hoverflyは,Client-"外部サービス"間でMiddlewareにより処理されたrequestとresponseを転送する環境を提供します.
Capture modeと違ってSimulationsの作成は行いません.

Diff mode

スライド5.PNG

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.comcurlした際に生成された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で生成したSumulationssimulation.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を利用することができます.

request-responseのキャッシュ機能

Hoverflyは,送られてきたrequestに対してマッチング検証を行うことで適切なresponseを選択しています.
しかし,送られてきたrequestに対して毎回検証を行うと,responseの選択と応答に時間がかかってしまいます.
そのため,Hoverflyは一度送られてきたrequestに対しては,選択されたresponseとともにキャッシュに残す機能があります.
ここでrequestヘッダは時間やclientによって変化しやすいので,デフォルトでは含まれません.
Simulationsが編集されたとき,キャッシュはリセットされます.

おわりに

いかがでしたでしょうか.
私自身初めてAPIシミュレーションツールを触ってみたのですが,思っていた以上に応用範囲が広そうだと感じました.
簡単にテスト用APIサーバを立てられる点を利用すれば,大規模開発でスタブとして使うだけでなく,ハッカソンでのモック作成でもきっと活躍してくれることだろうと思いました.

ということで,明日のAdvent Calenderでは『フロントエンド/バックエンドを分離して開発するためにHoverflyを利用してみる』を投稿させていただこうと思います.

【追記 2021/12/19】
公開完了しました!

Windows環境にてHoverflyの活用を試みています.
HTTPSを利用する具体的な方法もこちらに書かせていただきました.

ぜひ皆様のご参考になればと思います.

12
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?