发布于2026-07-19 阅读(0)
扫一扫,手机访问
写异步测试的时候,最怕什么?不是逻辑复杂,而是明明写了 async def test_xxx,pytest 跑完显示“passed”,但实际什么都没测——只因为忘了加 await,或者 pytest 根本不认识它。今天就把这块的坑和正确操作一次性说清楚,省得你对着 RuntimeWarning 发呆。
先看最常见的报错场景: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-asynciopytest.ini 或 pyproject.toml,显式启用插件(新版本 pytest 不再自动发现)asyncio_mode = auto,否则默认是 strict,遇到没标 @pytest.mark.asyncio 的 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 复用出错。
@pytest.mark.asyncio,否则 pytest 直接忽略event_loop fixture(比如要手动控制 loop)时,必须加 mark,否则 fixture 注入失败一句话:但凡你拿不准,先把 mark 加上,总比 debug 半天强。
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)asyncio.sleep(0) 触发调度,或用 time.perf_counter() 断言耗时范围,别硬等yield 和 return 怎么选async fixture 不能用 yield,因为 yield 语句无法在 async def 里直接用 —— 你会看到 SyntaxError: 'yield' inside async function。
正确方式是用 async def + return,清理逻辑单独写 teardown 函数,或用 async with 管理生命周期。
return connection,teardown 放在 test 结尾手动 await conn.close()async with MyResource() as r: yield r —— 注意这里 yield 在 async with 块内,外层仍是 async defawait,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 上的偶发失败。所以,scope 的配置一定要和你的实际资源生命周期对齐,别偷懒。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8