fastapi: 测试:RuntimeError: Task <...> got Future <...> attached to a different loop

一,报错信息

E           RuntimeError: Task <Task pending name='Task-6' coro=<_wrap_asyncgen_fixture.<locals>._asyncgen_fixture_wrapper.<locals>.finalizer.<locals>.async_finalizer() 
running at /data/python/fastapi/demo1/venv/lib/python3.10/site-packages/pytest_asyncio/plugin.py:416> 
cb=[_run_until_complete_cb() at /usr/local/soft/python3.10.19/lib/python3.10/asyncio/base_events.py:184]> 
got Future <Future pending> attached to a different loop

/usr/local/soft/python3.10.19/lib/python3.10/asyncio/streams.py:501: RuntimeError

RuntimeError: Task <Task pending name='Task-8' coro=<AsyncConnection.close() running at /data/python/fastapi/demo1/venv/lib/python3.10/site-packages/sqlalchemy/ext/asyncio/engine.py:483> 
cb=[shield.<locals>._inner_done_callback() at /usr/local/soft/python3.10.19/lib/python3.10/asyncio/tasks.py:847]> 
got Future <Future pending> attached to a different loop

 

二,原因:

这个错误的含义:

RuntimeError: Task <...> got Future <...> attached to a different loop:
测试套件在运行期间,创建了多个不同的事件循环(Event Loop),
而你的数据库连接池(Connection Pool)或某些全局异步资源,在一个旧的 Loop 里创建,却试图在另一个新的 Loop 里被关闭(或调用)。

 

原因:

在 pytest-asyncio 的默认机制中:

scope="function" 的 Fixture 和测试用例:每次执行一个独立的测试函数,pytest 就会销毁当前的 Event Loop,然后为下一个测试函数创建一个崭新的 Event Loop。

scope="session" 的 Fixture(比如你用来创建表结构的 init_test_database):
它是在测试刚开始时,在一个旧的(例如 Session 级别的)Event Loop 里创建了 SQLAlchemy 的 Engine(引擎)或连接池。

当你的测试用例运行完毕,pytest 准备销毁环境并关闭数据库连接(触发 AsyncConnection.close() 或 Fixture 的 finalizer)时,当前活动的 Event Loop 已经变了。
SQLAlchemy 的底层连接由于绑定了之前的旧 Loop,当它在新 Loop 里尝试操作时,异步框架就会崩溃并抛出:attached to a different loop。

三,解决:

配置 pytest.ini 的 asyncio_default_fixture_loop_scope
如果你使用的是 pytest-asyncio 较新的版本(v0.23+),官方引入了一个更优雅的配置项。
你不需要在代码里重写 event_loop 了,直接去你项目的根目录,在 pytest.ini 文件中添加以下两行配置:

[pytest]
asyncio_mode = auto
# 核心配置:将异步 fixture 的默认 loop 作用域提升为 function 级别,或者保持统一设置为session
asyncio_default_fixture_loop_scope = session

 

posted @ 2026-07-28 10:45  刘宏缔的架构森林  阅读(3)  评论(0)    收藏  举报