直接用 @pytest.mark.asyncio 会报 RuntimeError,因为 FastAPI 异步接口返回协程对象,而普通 pytest 测试函数不 await 协程,导致断言接收协程而非结果;必须配合 pytest-asyncio 插件,且测试函数需为 async def 并显式 await client.get(),TestClient 方法在该环境下才返回可 await 响应。

为什么直接用 pytest.mark.asyncio 会报 RuntimeError?
因为 FastAPI 的异步接口(比如 async def read_item())返回的是协程对象,而普通 pytest 测试函数是同步执行的——它不会 await 这个协程,而是直接把协程当普通对象传给断言,导致 RuntimeError: Task got bad yield: <coroutine object ...>。
必须显式让测试函数支持 async/await,且确保事件循环在测试生命周期内可用。
- 不能只加
@pytest.mark.asyncio就完事:需配合支持 asyncio 的 pytest 插件(如pytest-asyncio) - 不要手动调用
asyncio.run():FastAPI 的TestClient内部已管理事件循环,重复启动会冲突 - 别用
loop.run_until_complete():pytest-asyncio 已接管 loop,手动操作易出RuntimeError: This event loop is already running
如何正确写一个测试 FastAPI 异步路由的 pytest 函数?
核心是两点:用 TestClient 发起 HTTP 请求(它内部会自动 await),并让测试函数本身是 async + 加上 @pytest.mark.asyncio 标记。
示例代码(假设你有一个 /items/{item_id} 异步 GET 接口):
立即学习“Python免费学习笔记(深入)”;
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
import pytest
from fastapi.testclient import TestClient
from main import app # 假设你的 FastAPI 实例叫 app
<p>client = TestClient(app)</p><p>@pytest.mark.asyncio
async def test_read_item():
response = await client.get("/items/1")
assert response.status_code == 200
assert response.json() == {"item_id": "1", "name": "test"}</p>-
TestClient的.get()、.post()等方法在 pytest-asyncio 下返回可 await 的响应对象 - 必须用
await client.get(...),不能写成client.get(...)(否则 response 是协程,response.status_code会报 AttributeError) - 确保已安装
pytest-asyncio:运行pip install pytest-asyncio,否则@pytest.mark.asyncio不生效
测试带依赖(如数据库、Auth)的异步接口时怎么 mock?
FastAPI 的依赖注入系统在测试中默认仍会真实执行依赖——比如 Depends(get_db) 会真连数据库。你需要用 app.dependency_overrides 替换为 mock 版本。
关键点:override 必须在测试函数内设置,并在结束后清除,否则会影响其他测试:
@pytest.mark.asyncio
async def test_read_item_with_mock_db():
async def mock_get_db():
return MockSession()
<pre class='brush:python;toolbar:false;'>app.dependency_overrides[get_db] = mock_get_db
try:
response = await client.get("/items/1")
assert response.status_code == 200
finally:
app.dependency_overrides.clear() # 必须清理!- mock 的依赖函数也得是
async def,否则类型不匹配,FastAPI 会拒绝注入 - 不要在 conftest.py 或 fixture 中全局 override:不同测试可能需要不同 mock 行为,硬覆盖易出错
- 如果依赖里用了
yield(比如 async context manager),mock 也得用async with或async for模拟,否则会报TypeError: object ... can't be used in 'await' expression
为什么测试里 await client.post() 有时卡住或超时?
常见原因是被测接口里用了阻塞操作(比如 time.sleep(1)、同步 requests 调用、未 await 的数据库查询),导致整个事件循环被挂起。FastAPI 的 async route 必须全程异步,任何 sync 阻塞都会拖垮测试。
- 检查接口中是否误用了同步库:例如用
requests.get()替代aiohttp.ClientSession().get() - 确认 ORM 查询已 await:SQLModel/SQLAlchemy 2.0+ 的
session.exec()是同步的,要用await session.execute()+scalars().all() - 避免在测试中使用
time.sleep():改用await asyncio.sleep(0.01) - 如果用了第三方 async 依赖(如
httpx.AsyncClient),确保其 session 在测试生命周期内正确 close,否则连接池耗尽也会卡住
async 测试的脆弱性往往藏在“看起来没问题”的同步调用里,一旦漏掉一个 await,整个测试就变成假异步。

















