pytest不支持直接运行async def测试函数,需用pytest-asyncio插件或手动asyncio.run()(不推荐);HTTP客户端须用aiohttp/httpx并复用session;数据库驱动需真异步(如asyncpg/motor);禁用time.sleep(),改用await asyncio.sleep();避免同步库如selenium。

asyncio.run() 不能直接套在 pytest 测试函数上
pytest 默认不支持 async def 测试函数,直接写 async def test_api_call(): 会报 TypeError: object async_generator can't be used in 'await' expression 或直接被跳过。根本原因是 pytest 的测试收集器不认识协程对象,也不会自动 await 它。
解决办法只有两个:要么用 pytest-asyncio 插件(推荐),要么手动在同步函数里调用 asyncio.run() —— 但后者每次都会新建事件循环,开销大、无法复用连接池,还可能和某些异步库(比如 aiohttp 的 session)冲突。
- 装插件:
pip install pytest-asyncio - 在
pytest.ini或pyproject.toml中启用:加asyncio_mode = auto - 测试函数保持
async def,pytest 会自动识别并 await
HTTP 请求别用 requests,改用 aiohttp 或 httpx(带 async 支持)
requests 是同步阻塞的,哪怕包在 async def 里,一个请求也会卡住整个事件循环,彻底废掉异步收益。必须换真正基于 asyncio 的客户端。
aiohttp 成熟稳定,适合重度 HTTP 自动化;httpx API 更接近 requests,且同时支持 sync/async,迁移成本低。注意:两者 session 都必须复用,不能每个请求都新建 aiohttp.ClientSession() 或 httpx.AsyncClient(),否则连接池失效,反而比同步还慢。
立即学习“Python免费学习笔记(深入)”;
- 正确写法:用
pytest.fixture(scope="session")提供复用的httpx.AsyncClient() - 错误写法:
async with httpx.AsyncClient() as client:写在每个测试函数里 - 别忘了
await client.aclose()或用async with确保释放
数据库操作也要异步,但得看驱动是否真支持
不是所有 “async” 数据库驱动都真正非阻塞。比如 aiosqlite 底层仍是调用 SQLite 的 C 函数,只是用线程池包装,不算纯异步;而 asyncpg(PostgreSQL)或 motor(MongoDB)才是真正的 asyncio-native 驱动。
如果测试涉及数据库清理(如每次测前 truncate 表),同步操作会拖垮整体并发度。用 asyncpg 时,记得用 await conn.execute("TRUNCATE table_name"),而不是 conn.cursor().execute() 这类同步调用。
- 确认驱动是否真异步:查文档是否明确写 “native asyncio support”
- 连接池必须复用:
asyncpg.create_pool()或motor.motor_asyncio.AsyncIOMotorClient() - 避免在测试中混用
sqlite3+asyncio—— 不是性能问题,是逻辑陷阱
time.sleep() 是异步脚本的隐形杀手
测试里写 time.sleep(2),整个事件循环就停 2 秒,所有并发任务一起等。必须换成 await asyncio.sleep(2),才能让出控制权,其他协程继续跑。
更隐蔽的是第三方库里的同步 sleep,比如某些浏览器自动化库(selenium)的 driver.implicitly_wait() 或 time.sleep() 调用。这类库本质上和异步不兼容,强行塞进 async 测试只会让并发退化成串行。
- 等待元素出现,优先用
await asyncio.wait_for(..., timeout=5)包裹异步检查逻辑 - 完全规避
selenium:改用playwright的 async API(async_playwright) - CI 环境里尤其要检查日志,确认没有
BlockingIOError或意外的长延迟


















