Pytest如何实现全测试套件仅执行一次setup/teardown 适配异步SQLAlchemy Postgres
问题根因
- 你提供的终端输出中,重复执行的是作用域为函数级(输出中F标识)的
expectedfixture,你仅配置了engine和event_loop的session级作用域,未调整相关业务初始化fixture的作用域,因此会出现每个测试用例都触发重复setup/teardown的问题 - 异步场景下session级fixture需要和自定义的session级event_loop兼容,默认pytest-asyncio提供的event_loop是函数级,你已经自定义了session级event_loop,需确保所有异步fixture的作用域与之匹配。
修正方案
1. 调整conftest.py全局配置
补充完整fixture逻辑,将数据库初始化、全局数据加载等逻辑都封装为session级异步fixture:
import asyncio import pytest from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker from postgresdb import load_json, PostgresDb ENGINE = 'postgresql+asyncpg://postgres@localhost:5432/test_db' # session级事件循环,全量测试仅创建销毁一次 @pytest.fixture(scope='session') def event_loop(): loop = asyncio.new_event_loop() yield loop loop.close() # session级异步引擎 @pytest.fixture(scope='session') async def engine(): engine = create_async_engine(ENGINE, echo=False) yield engine # 全量测试结束后销毁引擎 await engine.dispose() # session级数据库会话,全局仅执行一次初始化/清理 @pytest.fixture(scope='session') async def db_session(engine): # setup阶段:建表、加载全局测试数据,全量测试仅执行一次 async with engine.begin() as conn: # 示例:同步建表逻辑通过run_sync执行 # await conn.run_sync(PostgresDb.metadata.create_all) # 执行你的全局数据加载逻辑 await load_json(conn, "your_test_data_path.json") # 创建全局会话供测试用例调用 async_session = async_sessionmaker(engine, expire_on_commit=False) async with async_session() as session: yield session # teardown阶段:清理测试数据,全量测试仅执行一次 async with engine.begin() as conn: # 示例:删表逻辑 # await conn.run_sync(PostgresDb.metadata.drop_all)
2. 调整业务fixture作用域
你输出中重复执行的expected fixture如果是用于加载测试预期结果,将其作用域修改为session即可,如果是参数化fixture,显式指定scope参数:
# 示例:在测试文件或conftest.py中修改expected fixture @pytest.fixture(scope='session', params=[0,10,33]) # 替换为你的参数列表 def expected(request): return request.param
验证方式
执行测试时添加--setup-show参数,可观察到session级fixture的标识变为S,全量测试过程中仅执行一次SETUP和TEARDOWN。
内容的提问来源于stack exchange,提问作者alphaomega
相关产品推荐
相关产品推荐

