直接 pip install pytest-xdist 即可安装,无需配置环境变量或修改 pytest 配置;常用命令为 pytest -n 4(指定4进程并行),但需注意测试间不能共享状态(如全局变量、临时文件、数据库连接等),否则结果不稳定。

pytest-xdist 安装和基础命令怎么写
直接 pip install pytest-xdist 就行,不用额外配环境变量或改 pytest 配置。装完后最常用的启动方式就是加 -n 参数指定进程数,比如 pytest -n 4 表示开 4 个子进程并行跑测试。
注意:不是所有测试都能自动并行——pytest-xdist 默认按 test function 粒度分发,但如果你的测试之间有共享状态(比如共用一个全局变量、临时文件名没隔离、数据库连接没 cleanup),就可能出错,这时候它不会报错,而是结果不稳定。
-
-n auto会用os.cpu_count()值,但别盲目信它;笔记本上开满核反而因 I/O 或内存争抢变慢 -
-n 2比-n 1快,但-n 8不一定比-n 4快,尤其测试本身是 I/O 密集型(比如 HTTP 请求、DB 查询) - Windows 上默认用 spawn 启动子进程,某些依赖全局 import 的模块(比如用了
torch或cv2)可能卡住或报AttributeError: module '__main__' has no attribute '__file__'
哪些测试不能直接用 -n 并行跑
pytest-xdist 不会帮你做线程/进程安全隔离。它只是把 test function 分到不同进程中执行,但每个进程内部还是单线程、同步跑的。所以以下几类测试要特别小心:
- 写同一份临时文件:比如多个 test 都调
open('tmp.json', 'w'),最后谁写谁赢,还可能报PermissionError - 读写同一个数据库表且没事务回滚:test_a 插入一条,test_b 查出来以为是自己的,实际是 test_a 留下的脏数据
- 修改模块级变量:比如在
conftest.py里定义了COUNTER = 0,然后每个 test 都COUNTER += 1,那值完全不可预期 - 依赖系统时间或随机种子没重置:
time.sleep(0.1)+random.random()组合在并发下容易触发断言飘移
简单判断法:单个 test 单独跑通过,但加 -n 2 后偶尔失败 → 很大概率存在隐式共享状态。
怎么让 fixture 在多进程下不冲突
pytest 的 @pytest.fixture(scope='session') 和 @pytest.fixture(scope='module') 在 xdist 下会被每个 worker 独立执行一次,而不是全进程共享一个实例。这点常被误解。
比如你写了:
import tempfile
@pytest.fixture(scope='session')
def tmpdir():
return tempfile.mkdtemp()
表面上看是 session 级,但每个 worker 都会调一次 mkdtemp(),生成不同路径。这不是 bug,是设计如此——xdist 要求 worker 完全隔离。
- 需要跨 worker 共享资源?别用 fixture,改用外部服务(如 Redis、PostgreSQL)或显式加锁(
filelock库) - 想控制 fixture 初始化时机?用
scope='session'+autouse=True+ 判断os.environ.get('PYTEST_XDIST_WORKER')是否为None(主进程才执行) - 避免在 fixture 里做副作用操作(如启动 Flask server),除非你确认每个 worker 都该起一份
常见报错和对应排查点
遇到失败先别急着调参数,先看错误是否和并发相关:
-
BrokenPipeError: [Errno 32] Broken pipe:通常是 stdout/stderr 被某个 worker 异常关闭,常见于 logging 配置没设好,或用了不支持多进程的 handler(比如FileHandler没加锁) -
ImportError: No module named 'xxx':worker 进程找不到包,检查是不是用了相对导入、sys.path动态修改,或者setup.py没正确声明packages - 测试卡死没输出:可能是某个 test 用了阻塞式 socket、没设 timeout 的 requests、或 multiprocessing.Queue.get() 等待永远不到的数据
-
INTERNALERROR> ValueError: signal only works in main thread:第三方库(比如旧版requests或celery)在子进程里注册了 signal handler,得升级或绕过
调试建议:先用 pytest -n 1 --log-cli-level=INFO 看日志是否正常,再逐步加 worker 数;实在不行,给可疑 test 加 @pytest.mark.xfail(reason='flaky under xdist') 隔离问题范围。
真正麻烦的从来不是怎么开 -n,而是怎么让每个 test 在独立进程里也像单测一样干净可靠。状态隔离比并发本身更花时间。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











