使用pytest-asyncio测试FastAPI路由报Event loop is closed错误
问题现象
测试用例中发起超过1次异步调用时,固定触发如下报错:
RuntimeError: Event loop is closed
已尝试参考通用方案重写event_loop fixture,所有调整均未生效,需排查遗漏的配置点。
现有配置信息
- 测试执行命令:
python -m pytest tests/ --asyncio-mode=auto
- 依赖版本(requirements.txt):
pytest==7.1.2 pytest-asyncio==0.18.3 pytest-html==3.1.1 pytest-metadata==2.0.1
- 测试用例代码(test.py):
async def test_user(test_client_fast_api): assert 200 == 200 # 首次接口调用正常 request_first = test_client_fast_api.post("/first_route") # 第二次接口调用触发 RuntimeError: Event loop is closed request_second = test_client_fast_api.post("/second_route")
- 自定义fixture代码(conftest.py):
@pytest.fixture() def event_loop(): try: loop = asyncio.get_running_loop() except RuntimeError: loop = asyncio.new_event_loop() yield loop loop.close()
问题根因
- 自定义
event_loopfixture默认使用函数级作用域,和FastAPI测试客户端test_client_fast_api的生命周期不匹配:首次请求执行完成后,函数级的event_loop fixture就会执行teardown逻辑关闭事件循环,第二次请求发起时已经没有可用的运行中事件循环。 - 现有event_loop逻辑存在缺陷:
asyncio.get_running_loop()会抓取当前上下文已存在的运行中循环(可能是其他fixture/框架持有的循环),teardown阶段强制关闭该循环会直接破坏其他组件的正常运行。 - pytest-asyncio 0.18.3版本在
--asyncio-mode=auto模式下不会主动校验异步fixture的作用域匹配关系,不会抛出显性的生命周期不匹配错误,只会静默触发事件循环关闭异常。
修复方案
- 调整
event_loopfixture的作用域,和test_client_fast_api的作用域保持完全一致(如果测试客户端是会话级fixture就设为scope="session",模块级就设为scope="module"),不要使用默认的函数级作用域。 - 重写event_loop逻辑,不要抓取外部已有的运行中循环,直接为fixture创建专属的事件循环,随fixture生命周期统一创建和销毁,参考代码:
@pytest.fixture(scope="session") # 作用域和test_client_fast_api对齐 def event_loop(): loop = asyncio.new_event_loop() yield loop loop.close()
- 校验
test_client_fast_api的fixture定义,确保其显式依赖event_loop,且二者作用域完全一致,避免出现生命周期错位。
内容的提问来源于stack exchange,提问作者Dmitriy_kzn
相关产品推荐
相关产品推荐

