fastapi:测试:一个完整的测试异步接口的例子
一,安装用到的库
$ pip install pytest pytest-asyncio httpx sqlalchemy aiomysql
二,代码
结构:

conftest.py
# tests/conftest.py (API 专有)
import asyncio
import pytest
import pytest_asyncio
from httpx import AsyncClient, ASGITransport
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from app.api.main import api_app
from app.core.database import Base, get_db
from main import app
# 使用独立的测试数据库
# TEST_DATABASE_URL = "mysql+aiomysql://root:rootpassword@127.0.0.1:3306/test_db"
TEST_DATABASE_URL = "mysql+aiomysql://root:rootpassword@localhost:3306/media_bank"
test_engine = create_async_engine(TEST_DATABASE_URL, echo=False)
TestingSessionLocal = async_sessionmaker(bind=test_engine, expire_on_commit=False)
# 初始化表结构(通常在测试套件开始时运行一次)
@pytest_asyncio.fixture(scope="session", autouse=True)
async def init_test_database():
async with test_engine.begin() as conn:
# 测试前:清空并重新创建所有表
await conn.run_sync(Base.metadata.drop_all)
await conn.run_sync(Base.metadata.create_all)
yield
async with test_engine.begin() as conn:
# 测试结束后:可选清理
await conn.run_sync(Base.metadata.drop_all)
# 核心:每次测试独立的 AsyncSession,并利用外部事务自动回滚
@pytest_asyncio.fixture(scope="function")
async def db_session() -> AsyncSession:
async with test_engine.connect() as connection:
# 开启一个根事务
transaction = await connection.begin()
# 将 session 绑定到这个连接上
async with TestingSessionLocal(bind=connection) as session:
yield session
# 测试完成后,无条件回滚!数据库不会留下任何痕迹
await transaction.rollback()
# 核心:异步 HTTP 客户端
@pytest_asyncio.fixture(scope="function")
async def async_client(db_session: AsyncSession):
# 重写 FastAPI 的依赖项,注入带有自动回滚功能的 db_session
async def _get_test_db():
yield db_session
api_app.dependency_overrides[get_db] = _get_test_db
# 使用 httpx.AsyncClient 替代 TestClient
async with AsyncClient(transport=ASGITransport(app=api_app), base_url="http://127.0.0.1:8000") as client:
yield client
api_app.dependency_overrides.clear()
test_users.py
import pytest
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession
from app.models import User
# 必须为异步测试指定 pytest 标记
pytestmark = pytest.mark.asyncio
async def test_login_user_success(async_client: AsyncClient, db_session: AsyncSession):
# 2. 调用异步接口
payload = {"username": "admin", "password": "123456"}
response = await async_client.post("/api/account/login", data=payload)
# 3. 断言
print("response:", response)
print("status_code:", response.status_code)
assert response.status_code == 200
三,测试效果:
$ pytest
=========================================================================== test session starts ===========================================================================
platform linux -- Python 3.10.19, pytest-9.1.1, pluggy-1.6.0
rootdir: /data/python/fastapi/demo1
configfile: pytest.ini (WARNING: ignoring pytest config in pyproject.toml!)
plugins: anyio-4.13.0, asyncio-1.4.0
asyncio: mode=auto, debug=False, asyncio_default_fixture_loop_scope=session, asyncio_default_test_loop_scope=function
collected 1 item
tests/api/test_users.py . [100%]
============================================================================ warnings summary =============================================================================
app/api/v1/users.py:68
/data/python/fastapi/demo1/app/api/v1/users.py:68: PydanticDeprecatedSince20: Support for class-based `config` is deprecated, use ConfigDict instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.13/migration/
class UserResponse(BaseModel):
app/api/v1/users.py:93
/data/python/fastapi/demo1/app/api/v1/users.py:93: PydanticDeprecatedSince20: Support for class-based `config` is deprecated, use ConfigDict instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.13/migration/
class AssetResponse(BaseModel):
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
====================================================================== 1 passed, 2 warnings in 0.42s ======================================================================
浙公网安备 33010602011771号