[英]How to create unit tests for a FastAPI endpoint that makes request to another endpoint?
我有一個 FastAPI 應用程序,它在處理特定請求的函數中向其他端點發出請求。
如何使用fastapi.testclient.TestClient
為此端點構建單元測試?
import fastapi
import requests
import os
app = fastapi.FastAPI()
# in production this will be a k8s service to allow for
# automatic scaling and load balancing.
# The default value works when the app is run using uvicorn
service_host = os.environ.get('SERVICE_HOST', 'localhost:8000')
@app.post('/spawner')
def spawn():
# an endpoint that makes multiple requests to set of worker endpoints
for i in range(4):
url = 'http://'+service_host+f'/create/{i}'
requests.post(url)
return {'msg': 'spawned the creation/update of 4 resources'}
@app.post('/create/{index}')
def worker(index: int):
# an endpoint that does the actual work of creating/updating the resource
return {'msg': f'Created/updated resource {index}'}
如何編寫單元測試取決於您具體要檢查的內容。
例如,從您的示例代碼中,如果您只想檢查您的/spawner
端點是否正確調用了您的/create
端點一定次數,您可以使用 Python 的unittest.mock.patch
來修補requests.post
調用,以便它不會進行實際調用,但您可以檢查它本應進行的調用。 (請參閱如何模擬請求和響應?以獲取有關如何模擬requests
庫進行的外部調用的示例。)
您仍然使用FastAPI 的TestClient
來調用您的端點,但是在修補.post
調用時:
from fastapi.testclient import TestClient
from main import app
from unittest.mock import patch
def test_spawn():
client = TestClient(app)
mocked_response = requests.Response()
mocked_response.status_code = 200
with patch("requests.post", return_value=mocked_response) as mocked_request:
response = client.post("/spawner")
# Expect that the requests.post was called 4 times
# and it called the /create/{index} endpoint
assert mocked_request.call_count == 4
called_urls = [call.args[0] for call in mocked_request.call_args_list]
assert called_urls[0].endswith("/create/0")
assert called_urls[1].endswith("/create/1")
assert called_urls[2].endswith("/create/2")
assert called_urls[3].endswith("/create/3")
# Expect to get the msg
assert response.status_code == 200
assert response.json() == {"msg": "spawned the creation/update of 4 resources"}
patch
返回一個Mock
對象,它具有諸如call_count
類的屬性,告訴您requests.post
函數被調用了多少次,以及一個call_args_list
存儲每次調用requests.post
的參數。 然后,您的測試還可以斷言,如果一切都按預期運行,那么它應該返回預期的響應。
現在,回到我所說的,這個測試的有用性在很大程度上取決於您希望從編寫測試中具體獲得什么。 這個測試的一個問題是它不是嚴格的黑盒測試,因為測試“知道”端點在內部應該如何工作。 例如,如果重點是確保使用某些查詢參數調用/spawner
?count=N
會產生N
個生成的資源,那么這種測試可能很有用。
另一件事,如果您正在對其他 API而不是對您自己的端點進行實際的外部調用,則這種測試是有意義的。 用戶 JarroVGit 在評論中提到更好的實現方式是,您可以重構/spawner
以直接調用端點函數並傳遞/create
響應,而不是發出 POST 請求:
@app.post("/spawner")
def spawn():
# an endpoint that makes multiple requests to set of worker endpoints
results = [worker(i) for i in range(4)]
return {
"msg": "spawned the creation/update of 4 resources",
"results": results,
}
@app.post("/create/{index}")
def worker(index: int):
# an endpoint that does the actual work of creating/updating the resource
worker_inst.create(index)
return {"msg": f"Created/updated resource {index}"}
通過修補/create
中的任何內容以不創建實際資源,可以使測試變得更簡單(在這里,我假設某種worker_inst.create(index)
進行實際創建):
def test_spawn():
client = TestClient(app)
# Patch the actual create/update resources to *not* create anything
with patch.object(WorkerInst, "create"):
response = client.post("/spawner")
# Expect to get the msg
assert response.status_code == 200
assert response.json() == {
"msg": "spawned the creation/update of 4 resources",
"results": [
{"msg": "Created/updated resource 0"},
{"msg": "Created/updated resource 1"},
{"msg": "Created/updated resource 2"},
{"msg": "Created/updated resource 3"},
],
}
我不知道你的應用程序正在創建什么樣的“資源”,但你可以將該單元測試轉換為更有用的集成測試,方法是刪除worker
的patch
-ing 並讓 worker 創建資源,然后讓測試斷言 1) 創建了正確的資源,並且 2) /spawner
端點返回了預期的響應。 同樣,取決於“資源”是什么以及測試的目的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.