在之前的 Requests 章節中,我們探討了軟體系統之間的頻繁溝通與互動。這些系統不僅能夠獲取來自其他系統的資料,還可以向它們發送請求以執行特定指令。而 Requests 套件則是處理 HTTP 傳輸協定時常用的工具之一。
對於 Test Code 而言,通常不希望在執行測試時實際與真實伺服器進行互動,這樣做可能會帶來以下幾個問題:
依賴外部服務的穩定性
測試結果會依賴於外部 API 的狀態。如果外部服務暫時中斷或出現問題,測試便會失敗,讓開發者誤以為是自己的 Product Code 有錯誤。此外,正如前文所提到的 FreezeGun,若外部 API 的回應數據會隨時間變化,測試結果也將因此變得不可預期和無法重複。
消耗 API 配額,增加測試成本
許多 API 都設有請求速率限制(rate limit)。如果每次測試都發送真實請求,則可能迅速達到配額上限,導致測試無法繼續進行。如果使用的 API 是付費服務,這將進一步增加測試的運行成本。
污染真實數據
直接發送真實請求可能會影響外部系統的數據,例如創建不必要的條目、修改或刪除重要資料,甚至可能引發安全問題。
本文將介紹一個名為 Responses 的套件。當 Product Code 透過 Requests 套件發送請求時,Responses 套件會攔截這些請求,並根據 Test Code 事先準備的假資料進行回應,從而模擬實際發送請求並接收回覆的過程。接下來,我將通過具體的範例進行詳細說明。
本次範例使用的是 Responses 0.25.3 版本
poetry add responses=0.25.3
首先,我們將準備一個 Product Code 函式,該函式會向貓咪網站發送請求。
import requests
API_URL = "https://catfact.ninja/fact"
def get_cat_fact() -> dict:
response = requests.get(API_URL)
response.raise_for_status()
return response.json()
接著,我們可以使用 Responses 套件來攔截真實請求。只需在需要攔截的測試函式上方添加 @responses.activate
Decorator,並創建要回傳的假資料。在本次測試中,我選擇模擬貓咪網站的回傳值為 cat is cute
。因此,當執行到函式 get_cat_fact
中的 requests.get(API_URL)
時,獲得的 JSON 回覆將是 {"fact": "cat is cute"}
,這樣每次測試的結果都將保持一致。
import responses
@responses.activate
def test_get_cat_fact():
# Arrange
responses.add(responses.GET, API_URL, json={"fact": "cat is cute"}, status=200)
# Act
result = get_cat_fact()
# Assert
assert result == {"fact": "cat is cute"}
最後我們可以執行 poetry run pytest
即可看到測試的結果