pytest运行async测试报RuntimeWarning是因为未await协程,需安装pytest-asyncio插件并配置asyncio_mode=auto,async测试函数建议加@pytest.mark.asyncio标记,尤其在class内或使用fixture时;应mock耗时协程而非真实IO;async fixture必须用async def+return,禁用yield。

pytest 运行 async test 报错 RuntimeWarning: coroutine 'test_xxx' was never awaited
这是因为 pytest 默认不识别 async def 函数,直接当普通函数调用,协程对象被创建但没 await,Python 就抛这个警告,测试还显示“passed”——实际根本没执行。
解决路径只有一条:让 pytest 知道该用 event loop 跑它。不用自己写 loop.run_until_complete,交给插件处理更稳。
- 装
pytest-asyncio:pip install pytest-asyncio - 在项目根目录加
pytest.ini或pyproject.toml,显式启用插件(新版本 pytest 不再自动发现) - 配置里必须指定
asyncio_mode = auto,否则默认是strict,遇到没标@pytest.mark.asyncio的 async test 会跳过或报错
async test 函数必须加 @pytest.mark.asyncio 吗
不一定,但强烈建议加。不加的前提是:你用了 asyncio_mode = auto,且函数名符合 pytest 默认匹配规则(如 test_*.py 里的 async def test_*)。
问题在于 “auto” 模式有边界:它只对模块级 async test 生效;如果 test 在 class 里、或函数带 fixture(尤其 session/scoped fixture),不加 mark 极大概率静默失败或 loop 复用出错。
立即学习“Python免费学习笔记(深入)”;
- class 内的 async test 必须加
@pytest.mark.asyncio,否则 pytest 直接忽略 - 用了
event_loopfixture(比如要手动控制 loop)时,必须加 mark,否则 fixture 注入失败 - 加了 mark 后,插件会确保每个 test 用干净的 event loop 实例,避免状态污染
测试中调用 asyncio.sleep() 或真实 IO 导致测试变慢
异步测试慢,往往不是因为 asyncio 本身,而是你在 test 里真发了 HTTP 请求、连了 DB、或用了 asyncio.sleep(1) 模拟延迟——这会让单个 test 卡 1 秒,批量跑就不可接受。
正确做法是 mock 掉耗时协程,而不是降速跑真实逻辑。
- 用
unittest.mock.AsyncMock替换依赖的 async 方法(Python 3.8+),例如:mock_obj.fetch_data = AsyncMock(return_value={"ok": True}) - 避免用
patch去 mock 整个 module,容易漏掉 import 路径;优先 patch 具体被测函数里 import 的位置(比如my_module.aioclient.get,而不是aiohttp.ClientSession.get) - 如果非得测真实 sleep 行为(比如验证重试逻辑),改用
asyncio.sleep(0)触发调度,或用time.perf_counter()断言耗时范围,别硬等
fixture 返回 async 对象时,yield 和 return 怎么选
async fixture 不能用 yield,因为 yield 语句无法在 async def 里直接用 —— 你会看到 SyntaxError: 'yield' inside async function。
正确方式是用 async def + return,清理逻辑单独写 teardown 函数,或用 async with 管理生命周期。
- 简单资源(如临时数据库连接):直接
returnconnection,teardown 放在 test 结尾手动await conn.close() - 需要自动清理:定义一个 async context manager,然后在 fixture 里
async with MyResource() as r: yield r—— 注意这里 yield 在 async with 块内,外层仍是async def - 别试图在 sync fixture 里
await,pytest 不允许;所有 async fixture 必须声明为@pytest.fixture(scope="...", autouse=True)并标记@pytest.mark.asyncio
最常被忽略的是 event loop 的 scope。function 级 test 默认用独立 loop,但如果你写了 session-scoped async fixture,多个 test 共享同一个 loop,中间任何未 await 的协程残留都会导致后续 test 报 RuntimeError: Event loop is closed——这种问题不会立刻暴露,得看 CI 上的偶发失败。


















