API レスポンスをモックする
アプリの API 呼び出しの一つに、あらかじめ用意したレスポンスを返す。手元のスマートフォン上で、偽サーバーも再ビルドも不要です。固定ボディなら Mock ブロックルール、ちょうど 1 回だけ発火させるなら実行回数の上限、ボディを計算する必要があるならスクリプト。
実機でのモックは、たいていの構成が崩れる場所です。スタブサーバーはノート PC 上にあり、スマートフォンはそこへ到達でき、アプリはそこを向いていなければなりません。Busymate DevTools はモックをキャプチャ経路そのものに置きます。ブロックルールがメソッドとホスト+パスのパターンに一致するすべてのリクエストを監視し、一致したものに自ら応答します。合成エラー、切断された接続、あるいはオリジンからの返答とまったく同じに見えるモックの成功です。上流サーバーには決して接続しません。
ルールは 2 か所で適用されるため、あらゆる接続モードがカバーされます。ブラウザ、プロキシ経由の Android、PAC モードの iOS にはプロキシサーバーが、VPN モードの iOS には端末上の VPN トンネルが適用します。グローバルルールはすべての端末に適用され、端末ごとのルールがその上に重なります。変更は Realtime で配信されるので、保存したルールは再接続なしでスマートフォン上で有効になります。
必要な
すでにキャプチャ中のスマートフォンと、偽装したいエンドポイント。
キャプチャがオンのペアリング済みスマートフォン。まず iPhone または Android のガイドに従ってください。
その端末の SSL プロキシリストにある API ホスト。パスで一致させるルールは、復号されたホストでしかパスを見られません。
応答したい呼び出しのメソッド、ホスト、パスと、アプリが返してほしいボディ。
スクリプト版のみ:ロールに管理者階層のスクリプト権限があること。通常の Mock ルールに特別なロールは不要です。
順番どおりに
ステップ 1〜4 で固定モックができます。5 と 6 は固定ボディでは扱えない 2 つのケース、1 回だけ発火させることと、応答を計算することを扱います。
- 01
ホストが復号されていることを確認する
ダッシュボードのフィードを開き、そのエンドポイントへの実際のリクエストを見つけます。その行に復号済みのボディが表示されていれば準備完了です。ホストがまだ暗号化と表示されるなら、行のホスト操作か端末の設定から端末の SSL プロキシリストに追加し、呼び出しをもう一度発生させてください。暗号化されたまま通過するホストでは、エンフォーサーはホスト名しか一致させられません。パスは見えないので、パス単位の精密なモックは決して発火しません。
- 02
Blocks を開いてルールを追加する
グローバルルールなら設定ナビゲーションから Blocks ページを開き、このスマートフォンだけに適用するルールなら端末を選んでから Blocks を開きます。ルールを追加し、メソッド(任意のメソッドに一致させるなら空欄)とパターンを入力します。パターンはブレークポイントと同じホスト+パスのワイルドカードです。ホスト、スラッシュ、パス、そして変動するセグメントにはアスタリスクを置きます。
- 03
Mock を選んでレスポンスを組み立てる
アクションを Mock に設定します。Block は呼び出しを失敗させるため、Drop はネットワークが消えたように見せるためのものですが、Mock は呼び出しを満たすためのものです。ステータス(既定は 200)、アプリが参照するヘッダー、そしてボディを設定します。アプリが解釈する Content-Type を与えてください。テキスト応答を受け取った JSON クライアントは 1 バイトも読む前に失敗します。JSON として保存すると、ルールは次のようになります。
フィーチャーフラグの呼び出しに固定ボディで応答する Mock ルール json{ "enabled": true, "method": "GET", "pattern": "api.example.com/v1/feature-flags", "action": { "type": "mock", "status": 200, "headers": { "Content-Type": "application/json" }, "body": "{\"flags\": {\"newCheckout\": true}}" } } - 04
保存し、呼び出しを発生させ、フィードを確認する
ルールを保存してアプリを使います。次に一致したリクエストはキャプチャ経路から一度も出ることなく応答され、それでもフィードにはモック済みとして表示されるので、何が偽装されたかを正確に確認し、ルールが期待どおりに動いたことを確かめられます。アプリには普通のサーバー応答しか見えません。合成レスポンスには内部用や識別用のヘッダーが一切含まれないため、レスポンスの中に介在を示すものはありません。
終わったら有効スイッチでルールをオフにします。無効化されたルールはコストがかからず、次回のために形を保ちます。
- 05
実行回数の上限で、ちょうど 1 回だけ発火させる
モックの中には 1 回だけ起きなければならないものがあります。典型例は、トークンが期限切れになったとアプリに思わせてリフレッシュフローを走らせる合成 401 です。すべてのリクエストにそう応答すると、アプリはリフレッシュして再試行し、また 401 を受け取り、永遠に回り続けます。Max runs フィールドはルールが発火できる回数を制限します。上限に達すると自動で無効化され、エディタには自動無効化のバッジが表示されます。自分でオフにしたルールとは区別されます。
カウントは端末ごとに記録され、再接続やアプリの再起動をまたいで保持されるので、上限 1 のルールはそのスマートフォンで通算 1 回だけ発火し、セッションごとではありません。上限付きのグローバルルールは、上限を使い切った端末でのみ抑止され、フリート内の他のすべてのスマートフォンでは発火し続けます。BusyBro に自然言語で、あるホストへの 1 回限りのモック 401 を頼むこともでき、同じルールを書いてくれます。
1 回限りの 401 — 通常のモックと同じで、上限 1 を加えただけ json{ "enabled": true, "method": "GET", "pattern": "api.example.com/v1/me", "action": { "type": "mock", "status": 401, "headers": { "Content-Type": "application/json" }, "body": "{\"error\": \"token_expired\"}" }, "maxRuns": 1 } - 06
スクリプトでボディを計算する
Mock のボディは固定された 1 つの文字列です。応答をリクエストに依存させる必要があるとき、たとえば ID をそのまま返す、タイムスタンプを入れる、実際の応答の 1 フィールドだけ書き換えるといった場合は、ルールのアクションを Script に切り替えるか、Settings、Scripts の順に開いて独立したスクリプトを作成します。リクエスト、レスポンス、ヘルパーオブジェクトの補完が効くコードエディタと、実際にキャプチャしたエントリに対してスクリプトを実行し、ライブ通信に触れずに前後の差分を表示する Test パネルが使えます。このドライランは本番プロキシと同一のサンドボックスを使うので、テストに通ることは本番で動くことを意味します。
例外を投げたりタイムアウトしたりしたスクリプトはフェイルオープンします。元のバイトがそのまま転送され、エントリはインスペクタでフラグ付けされるので、壊れたスクリプトが接続を詰まらせたりアプリに自身を露呈したりすることはありません。スクリプトはキャプチャ経路で任意のコードを実行するため、管理者階層です。
計算した JSON ボディで短絡するリクエストフック javascriptfunction onRequest(req) { if (req.path.startsWith("/v1/feature-flags")) { // computed per request — the upstream is never contacted return Response.json({ flags: { newCheckout: true, seed: Date.now() } }, { status: 200 }); } // no return → forward unchanged }
うまくいかない
動いていないように見えるルールは、ほぼ必ず自分には見えないものに一致させようとしています。
ルールが一度も発火しない
その端末でホストが復号されているか(パスのパターンには必須です)、次にパターンの綴りと、メソッドが一致しているか空であるかを確認します。実際のリクエストのフィード行に、コピーすべき正確なホストとパスが表示されています。
アプリがリフレッシュのループにはまった
合成 401 がすべてのリクエストで発火しています。Max runs を 1 に設定して、最初のヒットの後にルールが自動で無効化され、再試行されたリクエストが本物のサーバーに届くようにしてください。
アプリがモックした応答を拒否する
たいていは Content-Type ヘッダーがないか、ボディの形がクライアントの解釈と合っていません。インスペクタから実際にキャプチャした応答のヘッダーとボディをコピーして出発点にしてください。
ルールに自動無効化のバッジが付いている
この端末で実行回数の上限を使い切りました。Max runs をクリアするか、ルールを再度有効にすれば再び動きます。
スクリプトは実行されたが、応答は本物だった
スクリプトが例外を投げるかタイムアウトしてフェイルオープンしました。インスペクタでエントリを開き(エラーバッジにメッセージと行番号があります)、エディタで修正して、再度有効にする前に同じエントリに対して Test パネルを実行し直してください。
よくある
アプリはモックされたことに気づけますか?
レスポンスを調べても気づけません。モックした 200 やブロックした 403 には識別用のヘッダーがなく、オリジンからの普通の応答に見えます。可視性は一方向で、フラグ付きのエントリはあなたのフィードにだけ現れます。
VPN モードの iOS でも動きますか、それともプロキシ経由だけですか?
どちらでも動きます。同じルールエンジンが iOS の VPN トンネル内とプロキシサーバー上で動くので、どちらのモードの iOS も、プロキシ経由の Android も、ブラウザもすべてルールに従います。
ルールを変えたら何かを再デプロイする必要がありますか?
いいえ。ルールは Realtime で端末に流れるのと同じ設定に含まれるので、保存すれば新しい接続に即座に適用されます。すでに開いているキープアライブ接続は、再接続するまで開始時の設定を保持します。