Pytest Session级Fixture被多次计算的问题排查求助
问题场景
测试目录结构:
tests |__element | |__test_create.py | |__test_delete.py | |__test_get.py | |__test_recover.py | |__test_update.py |__conftest.py
在conftest.py中定义了session作用域的异步fixture,预期整个测试会话中仅初始化一次:
@pytest.fixture(scope="session") async def workspace_locales_set() -> list[str]: random_locales_subset = random.sample(list(LOCALES_ALLOWED.keys()), random.randint(3, 7)) print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", random_locales_subset) return random_locales_subset
实际运行时,该fixture在从test_recover切换到test_update时被第二次计算,导致test_update中获取的locales值变化,测试全部失败。当前使用pytest-asyncio,且配置asyncio_mode = auto。
可能的原因及解决办法
1. asyncio_mode=auto的行为冲突
当asyncio_mode=auto时,pytest-asyncio会自动为同步测试创建独立的事件循环,为异步测试创建另一个事件循环。session级异步fixture需要绑定到同一个事件循环,若不同测试模块的事件循环上下文不一致,会触发fixture重新初始化。
解决办法:
显式设置asyncio_mode = strict,强制所有测试使用异步模式,确保整个会话共用同一个事件循环。在pytest.ini或pyproject.toml中添加配置:
[pytest] asyncio_mode = strict
2. 测试用例存在同步/异步混合场景
如果test_recover.py或test_update.py中存在同步测试用例(用def而非async def定义),auto模式下会为同步测试单独创建事件循环,导致session级fixture在不同循环中被重复执行。
解决办法:
确保所有依赖该session fixture的测试用例均为异步实现(使用async def定义测试函数),或者统一处理同步测试的事件循环绑定逻辑。
3. Fixture依赖链存在作用域冲突
若其他低作用域(如function/class级)的fixture重复定义了workspace_locales_set,或间接依赖时错误地触发了重新初始化,也会导致该问题。
解决办法:
检查所有测试模块及conftest文件,确保没有重复定义同名fixture,且所有依赖该fixture的地方均直接引用session级版本。
4. 辅助排查:固定随机种子
虽然不是重复初始化的根本原因,但固定随机种子可以让排查过程更稳定,便于验证问题是否解决。修改fixture如下:
import random @pytest.fixture(scope="session") async def workspace_locales_set() -> list[str]: random.seed(42) # 固定随机种子,保证每次生成结果一致 random_locales_subset = random.sample(list(LOCALES_ALLOWED.keys()), random.randint(3, 7)) print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", random_locales_subset) return random_locales_subset
内容的提问来源于stack exchange,提问作者Prosto_Oleg

