-n 参数应根据CPU物理核心数、I/O特性及系统负载合理设置,通常-n 3~5最优;-n auto易因忽略负载和I/O争抢导致性能下降;需配合--capture=no等调试参数定位问题。

pytest-xdist 的 -n 参数到底怎么设才不翻车
别盲目填 -n auto 或直接写 -n 8,核数不是越多越好。Python 的 GIL 和测试用例的 I/O 特性会让高并发反而拖慢总耗时。
实操建议:
立即学习“Python免费学习笔记(深入)”;
-
-n auto会调用os.cpu_count(),但没考虑当前系统负载或测试是否密集 I/O(比如大量 mock 文件读写、HTTP 请求),实际常导致进程争抢磁盘或网络资源 - 推荐先用
-n 2和-n 4对比耗时,再逐步试探——多数中等规模项目(200–800 个 test function)在-n 3到-n 5区间达到最优 - 如果测试里混有 Selenium、数据库事务回滚、临时文件写入等强阻塞操作,
-n值应 ≤ CPU 物理核心数,而非逻辑线程数(比如 8 线程的 i7,物理核是 4,就别硬上-n 8)
测试失败时为什么看不到完整 traceback,只显示 Worker crashed
xdist 把测试分发到子进程执行,异常堆栈默认被截断,主进程只收到 worker 异常退出信号,不自动拉取子进程的 stderr。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 加
--tb=short或--tb=long不起作用——必须配合--dist=loadscope或--dist=loadfile让失败更集中,再用--capture=no暴露真实输出 - 真正有效的调试组合是:
pytest -n 2 --capture=no --log-level=INFO,这样子进程日志会实时刷到终端 - 如果用了
pytest-asyncio,注意event_loopfixture 默认不跨进程共享,会导致RuntimeError: Event loop is closed——需显式加@pytest.fixture(scope="session")并手动管理生命周期
为什么 conftest.py 里的 fixture 在 worker 进程里不生效
xdist 启动多个独立 Python 解释器进程,每个 worker 只加载自己路径下的 conftest.py,不会自动同步父进程的 fixture 注册。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 所有跨 worker 共享的 fixture 必须定义在**测试目录根路径下的
conftest.py**,且不能依赖模块级变量(如全局 dict)传值 - 避免在
conftest.py中做带副作用的初始化(如启动本地 Redis、写临时配置),因为每个 worker 都会执行一遍——容易端口冲突或文件覆盖 - 需要共享状态?用
pytest_configure(config)+config.cache(基于文件的跨进程缓存),或改用外部协调服务(如 Redis)而不是内存变量
Mac / Windows 上 -n 启动失败,报 OSError: [Errno 24] Too many open files
xdist 每个 worker 都会打开自己的日志、临时文件、socket 连接,macOS 默认 ulimit -n 是 256,Windows 的句柄限制也偏低,-n 4 就可能触顶。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- Mac 上临时提限:
ulimit -n 2048(加到~/.zshrc持久化) - Windows 用户别用 PowerShell 默认环境,改用 Windows Terminal + WSL2,或确保 pytest 运行在 CMD(PowerShell 对子进程句柄管理更激进)
- 更稳妥的做法:用
--maxprocesses=2替代-n(pytest-xdist ≥ 2.9),它会动态控流,避免瞬间打满句柄
多进程跑测试不是开开关的事——核数、I/O 类型、fixture 作用域、系统资源限制,四个点只要一个没对齐,-n 就从加速器变成拖油瓶。最常被忽略的是:你以为在并行跑测试,其实是在并行抢磁盘和网络。


















