模擬、存根、假物件與模擬器:用程式設計代理測試 API 呼叫

當你的程式碼呼叫 API 時,你需要一種方法來測試它,而不必等到真實 API 故障時才測試。 你有五種替代項目可以選擇。 人們對這些名稱的使用很寬鬆,以下說明本文如何使用這些名稱:

  • Stub:一個用於取代你的 HTTP 用戶端或 SDK 呼叫的行程內替身,回傳一個預設的答案。 它快速且可重複,並且測試你用來寫出該回應的邏輯。
  • Mock:一個也會記錄你的程式碼如何呼叫它的存根,讓你的測試能檢查呼叫內容。 它甚至延伸到一個存根。
  • 假伺服器:一個小型且運作中的伺服器,通常存在記憶體中,你的應用程式透過 HTTP 呼叫它,而非真實 API。 它會測試你的 HTTP 用戶端,但你必須將應用程式指向它的 URL。
  • 模擬器:服務的本地版本,通常由服務擁有者發布,對所支援的操作其行為類似真實服務。 在依賴它處理錯誤前,先確認它涵蓋哪些限制和故障。
  • 攔截代理:位於你的應用程式與 API 之間的網路上。 你的應用程式呼叫真實的 URL,代理伺服器會透過你定義的回應傳遞請求或回應部分請求。 你的應用程式必須透過代理伺服器傳送流量,並且在 HTTPS 上,信任代理的憑證。

如何測試呼叫 API 的程式碼

Approach 它測試什麼 它需要的內容 就在
存根或模擬 你針對特定回應的邏輯 你的測試框架 你在單元測試中測試商業邏輯、解析和錯誤分支
假伺服器 你的 HTTP 用戶端與序列化功能 在你的應用程式中設定基礎 URL 或設定開關 API 還不存在,或者你需要穩定的後端來做 UI 工作
Emulator 支援的操作其行為接近真實服務 不同的端點或 連接字串 服務的擁有者會提供一個,你則離線開發
使用測試帳號的 Real API 真正的東西 憑證、配額與資金 你要從頭到尾驗證主路徑
攔截 Proxy 在真實 URL 上執行的應用程式,包含 SDK 重試與回應標頭 代理設定與憑證信任 你測試失敗、限制和延遲,卻不改變應用程式

你需要這些中的一個以上。 存根能讓你的單元測試執行更快。 代理會告訴你當真實 API 出錯時,整個應用程式會做什麼。 想了解更多兩者如何結合,請參考 Dev Proxy 與單元測試的比較。

程式碼代理可建構的內容

當你要求程式設計代理讓你的應用程式能處理 API 呼叫失敗的情況,並展示它確實能運作時,它會替你選擇一個替代方案。 我們想知道是哪一個,所以用三個程式碼代理做了測試(210 次)。 這些任務用了像是「正確處理速率限制並證明它有效」、「在不花錢做真實 API 呼叫的情況下驗證」以及「在沒有 OpenAI 金鑰或網路連線的情況下執行」等說法。

  • 在 140 次要求可運作程式碼的執行中,有 76% 是代理程式手動建構出失敗情境:fetch 存根、httpx.MockTransport,或臨時的 HTTP 伺服器。
  • 在 105 次執行中,有 61 次是針對會呼叫 GitHub、OpenAI 或天氣 API 的應用程式,代理會為應用程式新增基礎網址或設定切換器,以便能連到其偽造的服務。
  • 在真實 API 可用且提示未排除使用它的 75 次執行中,測試該應用程式的真實 API URL 的次數為 0。

存根是單元測試的合理選擇。 空白是他們省略的部分。代理的存根函式回傳的是代理預期的錯誤,但可能與 API 傳送的錯誤不符。 還有它新增的開關,用來透過你的應用程式存取假船。

如何使用代理程式的測試項目

  • 保留用於你的邏輯的 stub 程式。 它們很快,還會測試代理程式寫的分支。
  • 請要求提供實際的錯誤格式。 請代理程式以提供者記錄的狀態碼、標頭和主體欄位為依據來模擬每個錯誤。 單純的 429 無法測試你的應用程式是否讀取 retry-after,或區分計費錯誤與速率限制。
  • 審查切換為測試新增。 如果代理程式只為了測試能找到假網址而新增基礎 URL 設定,請決定是否要將該設定納入你的生產程式碼。
  • 在真實網址上執行一次應用程式,並模擬失敗情況。 在你發布前,先檢查執行中的應用程式、SDK 以及重試政策如何處理 API 本身的錯誤。 要逐步檢視代理的錯誤處理,請參閱 如何驗證您的程式碼代理所撰寫的錯誤處理。

在你的應用程式中試試看

Dev Proxy 是一種用於開發的攔截式 Proxy。 它會回傳你為應用程式已呼叫的 URL 所定義的回應,且不會更改應用程式的程式碼。 在你的設定中啟用 MockResponsePlugin, devproxyrc.json:

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
  "plugins": [
    {
      "name": "MockResponsePlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "mocksPlugin"
    }
  ],
  "urlsToWatch": [
    "https://api.contoso.com/*"
  ],
  "mocksPlugin": {
    "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/mockresponseplugin.schema.json",
    "mocksFile": "mocks.json"
  }
}

接著在 mocks.json 中定義回應。 這個會回傳 503 預測端點的 Retry-After 標頭:

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/mockresponseplugin.mocksfile.schema.json",
  "mocks": [
    {
      "request": {
        "url": "https://api.contoso.com/v1/forecast*",
        "method": "GET"
      },
      "response": {
        "statusCode": 503,
        "headers": [
          {
            "name": "Retry-After",
            "value": "10"
          }
        ],
        "body": {
          "error": "Service unavailable"
        }
      }
    }
  ]
}

啟動 Dev Proxy,並照常執行你的應用程式:

devproxy --config-file devproxyrc.json

與模擬不符的請求會送往真正的 API。 當你需要一個還不存在的後端時,CrudApiPlugin 會用記憶體資料 模擬 CRUD API 。 若想隨機讓部分請求失敗,而不是每次都失敗,請參考 「使用隨機錯誤測試我的應用程式」。 要安裝 Dev Proxy,請參見 設定 Dev Proxy。

下一步

也請參閱