FastAPI教程 測試

2021-11-03 14:16 更新

感謝Starlette,測試FastAPI應(yīng)用程序變得簡單而愉快。

它基于Requests,因此非常熟悉和直觀。

有了它,您可以直接將pytest與FastAPI一起使用。

使用 TestClient

進(jìn)口TestClient。

創(chuàng)建一個(gè)TestClient傳遞給它的FastAPI應(yīng)用程序。

創(chuàng)建名稱以 開頭的函數(shù)test_(這是標(biāo)準(zhǔn)pytest約定)。

TestClient以與使用相同的方式使用對象requests。

assert使用您需要檢查的標(biāo)準(zhǔn) Python 表達(dá)式編寫簡單的語句(再次,標(biāo)準(zhǔn)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"}

提示

請注意,測試功能是正常的def,而不是async def。

而且對客戶端的調(diào)用也是普通調(diào)用,不是使用await.

這使您可以pytest直接使用而不會出現(xiàn)并發(fā)癥。

技術(shù)細(xì)節(jié)

您也可以使用from starlette.testclient import TestClient.

FastAPI提供相同starlette.testclient的fastapi.testclient,就像為你的方便,開發(fā)人員。但它直接來自Starlette。

提示

如果async除了向 FastAPI 應(yīng)用程序發(fā)送請求之外,還想調(diào)用測試中的函數(shù)(例如異步數(shù)據(jù)庫函數(shù)),請查看高級教程中的異步測試

分離測試

在實(shí)際應(yīng)用程序中,您可能會將測試放在不同的文件中。

而且您的FastAPI應(yīng)用程序也可能由多個(gè)文件/模塊等組成。

FastAPI應(yīng)用程序文件

假設(shè)您有一個(gè)main.py包含F(xiàn)astAPI應(yīng)用程序的文件:

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def read_main():
    return {"msg": "Hello World"}

測試文件

然后你可以有一個(gè)test_main.py包含你的測試的文件,并app從main模塊 ( main.py)導(dǎo)入你的:

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"}

測試:擴(kuò)展示例

現(xiàn)在讓我們擴(kuò)展這個(gè)例子并添加更多細(xì)節(jié)來看看如何測試不同的部分。

擴(kuò)展的FastAPI應(yīng)用程序文件

假設(shè)您有一個(gè)main_b.py包含F(xiàn)astAPI應(yīng)用程序的文件。

它有一個(gè)GET可能返回錯(cuò)誤的操作。

它有一個(gè)POST可能返回多個(gè)錯(cuò)誤的操作。

兩個(gè)路徑操作都需要一個(gè)X-Token標(biāo)頭。

from typing import Optional

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: Optional[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

擴(kuò)展測試文件

然后test_main_b.py,您可以像以前一樣使用擴(kuò)展測試:

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_item():
    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"}

每當(dāng)您需要客戶端在請求中傳遞信息而您不知道如何傳遞時(shí),您可以在requests.

然后你就在你的測試中做同樣的事情。

例如:

  • 要傳遞路徑或查詢參數(shù),請將其添加到 URL 本身。
  • 要傳遞 JSON 正文,dict請將Python 對象(例如 a )傳遞給參數(shù)json。
  • 如果您需要發(fā)送表單數(shù)據(jù)而不是 JSON,請改用data參數(shù)。
  • 要傳遞headers,請dict在headers參數(shù)中使用 a 。
  • 對于餅干,一個(gè)dict在cookies參數(shù)。

有關(guān)如何將數(shù)據(jù)傳遞到后端(使用requests或TestClient)的更多信息,查看請求文檔。

信息

請注意,TestClient接收可以轉(zhuǎn)換為 JSON 的數(shù)據(jù),而不是 Pydantic 模型。

如果您的測試中有 Pydantic 模型,并且您想在測試期間將其數(shù)據(jù)發(fā)送到應(yīng)用程序,則可以使用JSON Compatible Encoder 中jsonable_encoder描述的。

運(yùn)行

之后,您只需要安裝pytest:

pip install pytest


████████████████████████████████████████ 100%

它將自動檢測文件和測試,執(zhí)行它們,并將結(jié)果報(bào)告給您。

運(yùn)行測試:

pytest

================ test session starts ================
platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: /home/user/code/superawesome-cli/app
plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1
collected 6 items

████████████████████████████████████████ 100%

test_main.py ......                            [100%]

================= 1 passed in 0.03s =================


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號