FastAPI 教程翻译 - 用户指南 34 - 测试
FastAPI Tutorial - User Guide - Testing
Thanks to Starlette, testing FastAPI applications is easy and enjoyable.
多亏了 Starlette,测试 FastAPI 应用程序变得轻松而愉快。
It is based on Requests, so it’s very familiar and intuitive.
它基于 Requests,因此非常熟悉和直观。
With it, you can use pytest directly with FastAPI.
有了它,您可以直接将 pytest 与 FastAPI 一起使用。
Using TestClient
使用 TestClient
Import TestClient
.
导入 TestClient
。
Create a TestClient
passing to it your FastAPI.
创建一个 TestClient
传递给您的 FastAPI。
Create functions with a name that starts with test_
(this is standard pytest
conventions).
创建名称以 test_
开头的函数(这是标准的 pytest
约定)。
Use the TestClient
object the same way as you do with requests
.
使用 TestClient
对象的方式与处理 requests
的方式相同。
Write simple assert
statements with the standard Python expressions that you need to check (again, standard pytest
).
用您需要检查的标准 Python 表达式编写简单的 assert
语句(再次,标准 pytest
)。
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
Tip
提示
Notice that the testing functions are normal
def
, notasync def
.请注意,测试功能是正常的
def
,而不是async def
。And the calls to the client are also normal calls, not using
await
.并且到客户端的调用也是普通调用,不使用
await
。This allows you to use
pytest
directly without complications.这使您可以直接使用
pytest
而不会有任何复杂性。
Technical Details
技术细节
You could also use
from starlette.testclient import TestClient
.您也可以使用
from starlette.testclient import TestClient
。FastAPI provides the same
starlette.testclient
asfastapi.testclient
just as a convenience for you, the developer. But it comes directly from Starlette.FastAPI 提供与
fastapi.testclient
相同的starlette.testclient
,以方便开发人员。但它直接来自 Starlette。
Separating tests
分离测试
In a real application, you probably would have your tests in a different file.
在实际的应用程序中,您可能会将测试放在另一个文件中。
And your FastAPI application might also be composed of several files/modules, etc.
而且您的 FastAPI 应用程序也可能由多个文件 / 模块等组成。
FastAPI app file
FastAPI 应用程序文件
Let’s say you have a file main.py
with your FastAPI app:
假设您的 FastAPI 应用有一个文件 main.py
:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
Testing file
测试文件
Then you could have a file test_main.py
with your tests, and import your app
from the main
module (main.py
):
然后,您可以将带有测试的文件 test_main.py
从 main
模块(main.py
)导入 app
:
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
Testing: extended example
测试:扩展示例
Now let’s extend this example and add more details to see how to test different parts.
现在,让我们扩展该示例并添加更多详细信息,以查看如何测试不同的部分。
Extended FastAPI app file
扩展 FastAPI 应用文件
Let’s say you have a file main_b.py
with your FastAPI app.
假设您的 FastAPI 应用有一个文件 main_b.py
。
It has a GET
operation that could return an error.
它具有 GET
操作,可能会返回错误。
It has a POST
operation that could return several errors.
它具有 POST
操作,可能会返回多个错误。
Both path operations require an X-Token
header.
两个路径操作都需要一个 X-Token
header。
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
fake_secret_token = "coneofsilence"
fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}
app = FastAPI()
class Item(BaseModel):
id: str
title: str
description: str = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: str = Header(...)):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]
@app.post("/items/", response_model=Item)
async def create_item(item: Item, x_token: str = Header(...)):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=400, detail="Item already exists")
fake_db[item.id] = item
return item
Extended testing file
扩展测试文件
You could then have a test_main_b.py
, the same as before, with the extended tests:
然后,您可以使用扩展测试,拥有一个与以前相同的 test_main_b.py
:
from fastapi.testclient import TestClient
from .main_b import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
assert response.status_code == 200
assert response.json() == {
"id": "foo",
"title": "Foo",
"description": "There goes my hero",
}
def test_read_item_bad_token():
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_read_inexistent_item():
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}
def test_create_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
)
assert response.status_code == 200
assert response.json() == {
"id": "foobar",
"title": "Foo Bar",
"description": "The Foo Barters",
}
def test_create_item_bad_token():
response = client.post(
"/items/",
headers={"X-Token": "hailhydra"},
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
)
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_create_existing_token():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={
"id": "foo",
"title": "The Foo ID Stealers",
"description": "There goes my stealer",
},
)
assert response.status_code == 400
assert response.json() == {"detail": "Item already exists"}
Whenever you need the client to pass information in the request and you don’t know how to, you can search (Google) how to do it in requests
.
每当您需要客户在请求中传递信息而又不知道如何传递信息时,都可以在 requests
中搜索(Google)如何执行此操作。
Then you just do the same in your tests.
然后,您只需在测试中执行相同的操作即可。
E.g.:
例如:
-
To pass a path or query parameter, add it to the URL itself.
要传递 path 或 query 参数,请将其添加到 URL 本身。
-
To pass a JSON body, pass a Python object (e.g. a
dict
) to the parameterjson
.要传递 JSON 正文,请将 Python 对象(例如
dict
)传递给参数json
。 -
If you need to send Form Data instead of JSON, use the
data
parameter instead.如果您需要发送表单数据而不是 JSON,请使用
data
参数。 -
To pass headers, use a
dict
in theheaders
parameter.要传递 headers,请在
headers
参数中使用dict
。 -
For cookies, a
dict
in thecookies
parameter.对于 cookies,在
cookies
参数中有一个dict
。
For more information about how to pass data to the backend (using requests
or the TestClient
) check the Requests documentation.
有关如何将数据传递到后端(使用 requests
或 TestClient
)的更多信息,请参见 Requests 文档。
Info
信息
Note that the
TestClient
receives data that can be converted to JSON, not Pydantic models.请注意,
TestClient
接收的数据可以转换为 JSON,而不是 Pydantic 模型。If you have a Pydantic model in your test and you want to send its data to the application during testing, you can use the
jsonable_encoder
descibed in JSON Compatible Encoder.如果您的测试中有 Pydantic 模型,并且想在测试过程中将其数据发送到应用程序,则可以使用 JSON 兼容编码器。
Run it
运行
After that, you just need to install pytest
:
之后,您只需要安装 pytest
:
pip install pytest
It will detect the files and tests automatically, execute them, and report the results back to you.
它将检测文件并自动进行测试,执行它们,并将结果报告给您。
Run the tests with:
使用以下命令运行测试:
pytest