pytest-xdist引发资源竞争的根本原因是多个worker进程默认共享同一SQLite文件、临时目录或全局缓存,导致database is locked等错误;必须通过worker_id隔离资源、选用合适dist策略(如loadfile/loadscope/loadgroup)并避免session级非幂等操作。

为什么 pytest-xdist 会引发资源竞争
pytest-xdist 启动的是多个独立进程(不是线程),每个 worker 进程都重新导入测试模块、执行 fixture 初始化、访问文件或数据库——如果所有进程默认指向同一个 SQLite 文件、同一个临时目录、同一个全局缓存字典,就必然争抢。常见错误现象包括:sqlite3.OperationalError: database is locked、FileExistsError、断言值被意外覆盖、测试间状态污染导致偶发失败。
用 worker_id 隔离进程级资源
不能依赖 scope="session" 的 fixture 直接返回连接对象,因为不可序列化;必须在每个 worker 内部独立创建。关键动作是读取 request.config.workerinput["workerid"](如 "gw0"),再构造唯一路径或标识:
- SQLite 文件名带上
worker_id:用tempfile.mktemp(suffix=f"_{worker_id}.db") - 临时目录用
tmp_path / worker_id,而不是固定路径 - 避免在
conftest.py中初始化全局dict或list,改用tmp_path下的 JSON 文件做轻量共享(仅读) - 若需跨测试传递只读配置(如 schema),确保返回值是
dict/str等可序列化类型
选对 --dist 策略避免文件/类级冲突
默认 --dist=load 是动态分发,但可能把同一文件里多个耗时测试全塞进一个 worker,拖慢整体;更危险的是,它可能让两个 worker 同时操作同一个测试文件里的共享 fixture。实际应按资源依赖选:
librosa 是一个用于音频和音乐分析的 Python 库。它提供了特征提取、频谱分析、节拍跟踪、起始检测和音频可视化的工具,基于 NumPy 和 SciPy 构建,用于科学音频计算。
-
--dist=loadfile:适合每个test_*.py独立使用 DB/端口/配置文件的场景 -
--dist=loadscope:适合类内 fixture 共享状态、但类间必须隔离(如class TestDBWrite:和class TestDBRead:不该混跑) -
--dist=loadgroup:配合@pytest.mark.xdist_group(name="api")手动分组,强制相关测试进同一进程 - 绝对不要用
--dist=each跑常规测试——它让每个 worker 跑全部用例,纯属自找死锁
别碰全局状态,尤其在 conftest.py 里
很多竞态问题其实和并发无关,而是 fixture 设计反模式导致的:
- 删掉所有
os.environ["DEBUG"] = "1"类修改环境变量的代码,改用pytest --env=dev参数注入 - 禁止在
sessionscope fixture 中写文件、建连接、启动子进程——除非你明确做了worker_id分离 - 用
tmp_path(pathlib.Path)替代tmpdir,前者天然支持多进程隔离 - 如果某个 fixture 必须复用(如预生成大测试数据),确保它是幂等的、只读的、且返回可序列化对象
真正难处理的不是“怎么加锁”,而是“哪些状态你以为是私有的,其实被所有 worker 共享了”。比如一个 @pytest.fixture(scope="session") 返回了 sqlite3.connect("test.db"),表面上看只初始化一次,实际上每个 worker 都试图连同一个文件——SQLite 的锁机制立刻报错。这种隐式共享,比显式并发更容易漏掉。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










