直接用 @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 ...></coroutine>。
必须显式让测试函数支持 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 接口):
import pytest
from fastapi.testclient import TestClient
from main import app # 假设你的 FastAPI 实例叫 app
<p>client = TestClient(app)</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill8551" title="Claw Code Suite (Python-Only Edition)"><img
src="https://img.php.cn/upload/skill/000/000/081/179212160094080.jpg" alt="Claw Code Suite (Python-Only Edition)" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill8551" title="Claw Code Suite (Python-Only Edition)" class="overflowclass">Claw Code Suite (Python-Only Edition)</a>
<p class="overflowclass">仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...</p>
</div>
<a rel="nofollow" href="/xiazai/skill8551" title="Claw Code Suite (Python-Only Edition)" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><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,整个测试就变成假异步。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










