我正在尝试使用 pytest 来测试使用 FastApi 在 python 中创建的简单 API。 我遇到两个问题:
Annotated
和Depends
声明输入参数,我无法使用pytest
运行测试。路由响应 422 Unprocessable Entity 状态代码。> DeprecationWarning: The 'app' shortcut is now deprecated. Use the explicit style 'transport=ASGITransport(app=...)' instead.
请让我知道如何解决这两个问题。我读到了关于 dependency override 的内容,但我不确定这是否是强制性的,或者我不确定如何通过改变该方法中的输入来为我的应用程序创建多个测试用例(例如:针对不同的人进行测试,以防我的 API 应返回不同的输出)每个)。
为了重现这个错误,我在
Python 3.10.13
中创建了一个简单的api。该 API 有两种使用 POST 方法的路由,其中一种直接将输入参数声明为 Person
类的 Pydantic 模型(我可以测试此路由,并带有弃用警告),而另一种则使用 Annotated[Person, Depends()]
(我可以测试此路由,并带有弃用警告)不知道怎么测试这个路由,测试失败):
这是我的简单 api 的 main.py 文件:
# simple_api/main.py
from fastapi import FastAPI, Depends
from pydantic import BaseModel, Field
from typing import Annotated
# Define a simple Pydantic v2 model to validate the post request
class Person(BaseModel):
name: str
age: int = Field(..., ge=18, le = 130)
# Load app
app = FastAPI()
# I can test this route:
@app.post("/post_no_dependency")
def post_example_no_dependency(person: Person):
return {
"message": f"Hello {person.name}, your age is {person.age}",
"method": "no dependency"
}
# But I am not sure how to test this route:
@app.post("/post_with_dependency")
def post_example_with_dependency(
person: Annotated[Person, Depends()]
):
return {
"message": f"Hello {person.name}, your age is {person.age}",
"method":"with dependency"
}
这是我的简单 api 的测试文件:
# /simple_api/test_simple_api.py
import pytest
from httpx import AsyncClient
from main import app
@pytest.fixture
async def async_client():
"""Fixture to create a FastAPI test client."""
async with AsyncClient(app=app, base_url="http://test") as async_test_client:
yield async_test_client
# This test works
@pytest.mark.anyio
async def test_post_example_no_dependency(async_client: AsyncClient):
data = {"name": "john doe",
"age": 32}
response = await async_client.post("/post_no_dependency",
json = data)
assert response.status_code == 200
assert response.json()["method"] == "no dependency"
# This test fails because the status code is 422, not 200.
@pytest.mark.anyio
async def test_post_example_with_dependency(async_client: AsyncClient):
data = {"name": "john doe",
"age": 32}
response = await async_client.post("/post_with_dependency",
json = data)
assert response.status_code == 200
assert response.json()["method"] == "with dependency"
这是我在 bash 终端中运行 pytest 时发生的情况(我使用的是 github 代码空间):
(simple_venv) @mikeliux ➜ /workspaces/simple_api $ pytest test_simple_api.py
==================================================================== test session starts ====================================================================
platform linux -- Python 3.10.13, pytest-8.1.1, pluggy-1.4.0
rootdir: /workspaces/simple_api
plugins: anyio-4.3.0
collected 4 items
test_simple_api.py .F.F [100%]
========================================================================= FAILURES ==========================================================================
________________________________________________________ test_post_example_with_dependency[asyncio] _________________________________________________________
async_client = <httpx.AsyncClient object at 0x7f0e0cd893f0>
@pytest.mark.anyio
async def test_post_example_with_dependency(async_client: AsyncClient):
data = {"name": "john doe",
"age": 32}
response = await async_client.post("/post_with_dependency",
json = data)
> assert response.status_code == 200
E assert 422 == 200
E + where 422 = <Response [422 Unprocessable Entity]>.status_code
test_simple_api.py:30: AssertionError
__________________________________________________________ test_post_example_with_dependency[trio] __________________________________________________________
async_client = <httpx.AsyncClient object at 0x7f0e0c436770>
@pytest.mark.anyio
async def test_post_example_with_dependency(async_client: AsyncClient):
data = {"name": "john doe",
"age": 32}
response = await async_client.post("/post_with_dependency",
json = data)
> assert response.status_code == 200
E assert 422 == 200
E + where 422 = <Response [422 Unprocessable Entity]>.status_code
test_simple_api.py:30: AssertionError
===================================================================== warnings summary ======================================================================
test_simple_api.py::test_post_example_no_dependency[asyncio]
test_simple_api.py::test_post_example_with_dependency[asyncio]
test_simple_api.py::test_post_example_no_dependency[trio]
test_simple_api.py::test_post_example_with_dependency[trio]
/workspaces/simple_api/simple_venv/lib/python3.10/site-packages/httpx/_client.py:1426: DeprecationWarning: The 'app' shortcut is now deprecated. Use the explicit style 'transport=ASGITransport(app=...)' instead.
warnings.warn(message, DeprecationWarning)
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================================================== short test summary info ==================================================================
FAILED test_simple_api.py::test_post_example_with_dependency[asyncio] - assert 422 == 200
FAILED test_simple_api.py::test_post_example_with_dependency[trio] - assert 422 == 200
========================================================== 2 failed, 2 passed, 4 warnings in 0.77s ==========================================================
此外,您可以在这里检查已安装的软件包:
(simple_venv) @mikeliux ➜ /workspaces/simple_api $ pip freeze
annotated-types==0.6.0
anyio==4.3.0
attrs==23.2.0
certifi==2024.2.2
click==8.1.7
dnspython==2.6.1
email_validator==2.1.1
exceptiongroup==1.2.0
fastapi==0.110.0
h11==0.14.0
httpcore==1.0.5
httptools==0.6.1
httpx==0.27.0
idna==3.6
iniconfig==2.0.0
itsdangerous==2.1.2
Jinja2==3.1.3
MarkupSafe==2.1.5
orjson==3.10.0
outcome==1.3.0.post0
packaging==24.0
pluggy==1.4.0
pydantic==2.6.4
pydantic-extra-types==2.6.0
pydantic-settings==2.2.1
pydantic_core==2.16.3
pytest==8.1.1
python-dotenv==1.0.1
python-multipart==0.0.9
PyYAML==6.0.1
sniffio==1.3.1
sortedcontainers==2.4.0
starlette==0.36.3
tomli==2.0.1
trio==0.25.0
typing_extensions==4.10.0
ujson==5.9.0
uvicorn==0.29.0
uvloop==0.19.0
watchfiles==0.21.0
websockets==12.0
提前谢谢您
我通过在测试文件中进行以下更改成功解决了附带问题(
DeprecationWarning
):
# /simple_api/test_simple_api.py
import pytest
from httpx import AsyncClient, ASGITransport # additional import
from main import app
@pytest.fixture
async def async_client():
"""Fixture to create a FastAPI test client."""
# instead of app = app, use this to avoid the DeprecationWarning:
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as async_test_client:
yield async_test_client