asyncio.run()在测试中报错是因为pytest等框架已在主线程运行事件循环,而asyncio.run()试图新建循环导致冲突;应改用pytest-asyncio插件标记异步测试或继承isolatedasynciotestcase。

asyncio.run() 在测试中为什么报 “RuntimeError: asyncio.run() cannot be called from a running event loop”
因为 pytest 或 unittest 默认在主线程的事件循环中运行测试,而 asyncio.run() 会尝试启动一个新循环,但当前已有循环在运行,直接调用必然失败。
常见于:在 setUp()、tearDown() 或普通测试方法里写 asyncio.run(some_async_func())。
- 不要在已处于事件循环中的上下文里再调用
asyncio.run() - 测试框架(如 pytest-asyncio)通常已为你管理好循环生命周期,优先复用它
- 若手动控制循环,必须确保
asyncio.get_event_loop()返回的是当前线程默认循环,且未被关闭
pytest-asyncio 的 @pytest.mark.asyncio 怎么用才不踩坑
这是最轻量、兼容性最好的方案,但依赖正确配置和标记习惯。
关键点:
- 必须安装
pytest-asyncio并在pytest.ini或pyproject.toml中启用插件(否则标记无效) - 测试函数必须是
async def,且仅标记在函数上,不支持类或模块级标记(除非用autouse=True配合scope) - 避免在同一个测试文件里混用同步/异步测试而不加标记——未标记的 async 函数会被当同步函数执行,导致
coroutine was never awaited警告 - 如果测试需要共享 fixture(比如数据库连接),fixture 也得是 async,并用
@pytest.fixture+@pytest.mark.asyncio组合,或直接设autouse=True
示例:
@pytest.mark.asyncio
async def test_fetch_user():
result = await fetch_user(123)
assert result.name == "Alice"
手动管理事件循环时,asyncio.new_event_loop() 和 asyncio.set_event_loop() 为何仍可能出错
手动创建循环看似可控,但容易忽略线程绑定和清理问题,尤其在 pytest 的多测试并行或重用进程中。
-
asyncio.new_event_loop()创建的循环默认未与当前线程绑定,需显式调用asyncio.set_event_loop(loop) - 测试结束后必须调用
loop.close(),否则下次测试可能拿到已关闭的循环,抛出RuntimeError: Event loop is closed - 在
setUp()创建、tearDown()关闭时,要确保每个测试独占循环——pytest 默认复用同一进程,不同测试间不自动隔离循环 - 更安全的做法是:在
setUp()中检查asyncio.get_event_loop().is_closed(),若关闭则新建并设为默认;但不如直接用pytest-asyncio省心
unittest 中跑 async 测试,为什么 AsyncMock 不解决事件循环问题
AsyncMock 只模拟协程行为,不提供执行环境。它不能帮你启动或注入事件循环。
真正卡点在于:unittest 的 TestCase 是同步基类,它的 run() 方法不会 await 你的 async 方法。
- 不要写
async def test_xxx(self):然后指望 unittest 自动处理——它会跳过或报错 - 可行路径只有两条:改用
pytest-asyncio(推荐),或在 unittest 中用asyncio.get_event_loop().run_until_complete()显式驱动 - 后者要注意:必须确保调用发生在未运行的循环中;若已在循环中(如被 pytest-asyncio 启动),会触发嵌套错误
- 简单封装可复用的基类(如继承
unittest.IsolatedAsyncioTestCase,Python 3.8+ 内置),它会在每个测试前后自动管理独立循环
示例(Python ≥3.8):
class TestAPI(unittest.IsolatedAsyncioTestCase):
async def test_create_item(self):
resp = await create_item({"name": "test"})
self.assertEqual(resp.status, 201)
事件循环冲突的本质不是语法问题,而是执行上下文归属没理清:谁启的环、谁关的环、谁该 await、谁只负责调度。多数情况下,放弃手动管理,交给 pytest-asyncio 或 IsolatedAsyncioTestCase,反而最不容易漏掉清理逻辑。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











