pytest不原生支持async测试函数,需安装pytest-asyncio插件并用@pytest.mark.asyncio装饰器标记,否则会被跳过或报错;Python 3.11对事件循环更严格,需注意event loop策略、async fixture写法及CI环境兼容性。

pytest 本身不原生支持 async def 测试函数
直接写 async def test_something(),pytest 会跳过它,或者报 TypeError: object async_generator can't be used in 'await' expression —— 因为 pytest 的默认 runner 不认识协程对象。这不是你代码写错了,是执行器没配对。
必须显式启用异步支持,主流方式是用 pytest-asyncio 插件,且要注意 Python 3.11 的 event loop 行为变化(比如 asyncio.run() 默认使用 asyncio.TrioEventLoop 的兼容性问题)。
- 安装插件:
pip install pytest-asyncio - 在测试文件顶部或
conftest.py中加配置,否则插件不生效:import pytest pytest_plugins = ["pytest_asyncio"]
- 测试函数需加
@pytest.mark.asyncio装饰器,不能只靠函数名或位置识别
async def 测试函数必须用 @pytest.mark.asyncio 标记
没加这个标记,pytest 就当它是普通函数处理,结果要么被跳过,要么抛出 RuntimeWarning: coroutine 'test_xxx' was never awaited。标记不是可选的“增强”,而是触发插件接管执行的开关。
示例正确写法:
import pytest
@pytest.mark.asyncio
async def test_fetch_data():
result = await some_async_function()
assert result == "ok"
- 不要写成
def test_xxx(): async def inner(): ...—— 嵌套协程不会被识别 - 标记必须作用于测试函数本身,不能只标在 fixture 上
- 如果用了自定义 event loop 策略(比如在
conftest.py里调用asyncio.set_event_loop_policy()),要确保和pytest-asyncio兼容,Python 3.11 默认策略更严格,容易报RuntimeError: asyncio.run() cannot be called from a running event loop
async fixture 需要同样标记,并注意 scope 和 cleanup
async fixture 不能靠 yield 做 teardown,因为 yield 返回的是 generator,不是协程。得用 async with 或显式 await cleanup 逻辑。
正确写法示例:
@pytest.mark.asyncio
@pytest.fixture
async def db_connection():
conn = await create_db_connection()
try:
yield conn
finally:
await conn.close()
- fixture 函数本身要加
@pytest.mark.asyncio,否则 pytest 不会 await 它 -
scope="session"的 async fixture 在 Python 3.11 下可能出问题:session 级 event loop 生命周期难管理,建议优先用scope="function" - 避免在 async fixture 里调用
asyncio.run()—— 它会尝试启动新 loop,而当前已有 loop 运行中,直接报错
常见错误:混合 sync/async 断言或 setup 导致 loop 关闭异常
比如在 setup_method 里手动 asyncio.run(setup()),再在测试里用 @pytest.mark.asyncio,就会触发 RuntimeError: asyncio.run() cannot be called from a running event loop。根本原因是 Python 3.11 对嵌套 loop 启动更敏感。
- 统一用
@pytest.mark.asyncio驱动所有异步操作,不要混用asyncio.run() - 如果必须在非 async 上下文初始化资源(比如模块级 setup),改用
asyncio.get_event_loop().run_until_complete(...),但仅限 loop 尚未关闭时 - CI 环境中(如 GitHub Actions)有时默认使用
uvloop,若未安装或版本不匹配,pytest-asyncio可能静默失败 —— 建议显式在pyproject.toml中声明[tool.pytest.ini_options] asyncio_mode = "auto"并锁定pytest-asyncio版本 ≥ 0.23.0
最易忽略的一点:Python 3.11 的 asyncio.TaskGroup 在测试中若未 await 完全,会导致 fixture teardown 卡住或静默丢弃异常 —— 记得在 async fixture 的 finally 块里检查 task 是否 done。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











