FastAPI 教程翻译 - 用户指南 34 - 测试

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.

有了它,您可以直接将 pytestFastAPI 一起使用。

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, not async 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 as fastapi.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.pymain 模块(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.

    要传递 pathquery 参数,请将其添加到 URL 本身。

  • To pass a JSON body, pass a Python object (e.g. a dict) to the parameter json.

    要传递 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 the headers parameter.

    要传递 headers,请在 headers 参数中使用 dict

  • For cookies, a dict in the cookies parameter.

    对于 cookies,在 cookies 参数中有一个 dict

For more information about how to pass data to the backend (using requests or the TestClient) check the Requests documentation.

有关如何将数据传递到后端(使用 requestsTestClient)的更多信息,请参见 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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值