能测但默认不生效——必须显式启用 pytest-asyncio 并配置 asyncio_mode = "auto",否则 async def 测试会被跳过或报 RuntimeError;推荐在 pyproject.toml 中配置 [tool.pytest.ini_options] 下的 asyncio_mode 和 fixture 循环作用域。

能测,但默认不生效——必须显式启用 pytest-asyncio 插件并配置事件循环策略,否则 async def 测试函数会被跳过或报 RuntimeError: no running event loop。
pytest-asyncio 没生效的典型表现
跑 async def test_something(): 时,pytest 完全无视它(输出里没有这条测试),或者直接报错:RuntimeError: no running event loop。这不是代码写错了,是插件没加载或配置不对。
- 没装
pytest-asyncio:执行pip install pytest-asyncio - 装了但没启用:pytest 默认不自动激活该插件,需在
pytest.ini、pyproject.toml或命令行显式指定 - 用了
async def却没加@pytest.mark.asyncio(旧版方式)或没配asyncio_mode = auto(新版推荐)
pyproject.toml 里最稳妥的配置方式
用 TOML 配置比 pytest.ini 更可靠,尤其在混合使用 Poetry 或现代 Python 工具链时。关键不是“支持 async”,而是让 pytest 主动识别并接管异步测试生命周期。
- 必须写
[tool.pytest.ini_options],不是[pytest] - 设
asyncio_mode = "auto":这样所有async def test_*自动被识别,无需每个函数都加@pytest.mark.asyncio - 可选加
asyncio_default_fixture_loop_scope = "function":避免 fixture 复用导致事件循环状态污染
[tool.pytest.ini_options] asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function"
async def 测试函数里 await 什么才安全
不是所有异步调用都能直接 await。核心原则:只 await 真正需要事件循环调度的协程,别混入同步阻塞操作,否则会卡住整个测试进程。
立即学习“Python免费学习笔记(深入)”;
- ✅ 安全:await
asyncio.sleep(0.1)、awaithttpx.AsyncClient().get(...)、await 自己写的async def函数 - ❌ 危险:await
time.sleep(1)(同步阻塞)、直接调用requests.get(...)(同步 HTTP)、在async def里用threading.Lock().acquire()(不是协程) - ⚠️ 注意:
asyncio.run()在测试里基本不用——pytest-asyncio 已为你管理好事件循环,再套一层会报RuntimeError: asyncio.run() cannot be called from a running event loop
fixture 中 return await 和 yield await 的区别
异步 fixture 是易错高发区。返回值时机决定资源是否被正确清理。
-
return await some_async_init():初始化完立刻返回,teardown 需另写async deffixture +yield配合await清理 -
yield await some_async_init():不合法!yield不能直接跟await,会语法错误 - 正确写法:用
async deffixture +yield,并在yield前后分别await初始化和清理逻辑
@pytest.fixture
async def db_connection():
conn = await async_db_connect()
try:
yield conn
finally:
await conn.close()
事件循环的作用域、fixture 的生命周期、以及 asyncio_mode 的实际生效位置,三者稍有错位就会静默失败。别依赖“看起来跑通了”,检查 pytest 输出里是否有 test_something PASSED 而不是被跳过,才是真生效。


















