Python 并行测试的 pytest-xdist 配置

2次阅读

pytest-xdist需单独安装,未安装会导致importerror;-n auto按cpu核数减1启动进程,但受测试间资源冲突限制;应避免共享文件、端口、数据库,改用临时路径、内存sqlite或随机后缀;–dist=loadgroup按类或标记分组调度,适合有状态依赖的测试。

Python 并行测试的 pytest-xdist 配置

pytest-xdist 启动就报 ImportError: No module named 'xdist'

这是环境没装对,不是插件没启用。pytest-xdist 是独立包,不随 pytest 自带,必须显式安装。

  • pip install pytest-xdist,别只装 pytest
  • 如果用虚拟环境,确认当前 shell 激活的是目标环境(which pythonwhich pytest 要一致)
  • 某些旧项目用了 setup.pyrequirements.txt,但漏了 pytest-xdist —— 它不会被自动推导依赖

加了 -n auto 却只跑 1 个进程

-n auto 不是“尽量多开”,而是按 os.cpu_count() 算出逻辑核数后减 1(留一个给主控),但实际能用多少还取决于测试本身是否真能并行。

  • 若测试里有全局锁、共享文件写入、time.sleep() 或硬编码的端口(如 8000),pytest-xdist 会退化为串行执行(或直接失败)
  • macosos.cpu_count() 返回的是逻辑核数,但 M 系列芯片上部分 Python 版本存在识别偏差,可改用 -n 2-n 4 显式指定
  • CI 环境(如 github Actions)默认只分配 2 核,-n auto 就是 1,得看 CI 配置再调

测试间相互干扰:数据库连接冲突、临时文件覆盖

pytest-xdist 把测试分发到多个子进程中,它们默认共用同一工作目录和环境变量,但不共享内存 —— 所以文件/端口/数据库这类外部资源极易撞车。

  • 避免硬编码路径:/tmp/test.db 改成 tempfile.mktemp(prefix=f"test_{os.getpid()}_")
  • 数据库用内存 SQLite(sqlite:///file::memory:)或每个 worker 加随机后缀:sqlite:///test_{worker_id}.db,其中 worker_id 可通过 os.environ.get("PYTEST_XDIST_WORKER") 获取
  • http 测试别固定端口,用 0 让系统自动分配:http.server.HTTPServer(("", 0), handler)

--dist=loadgroup--dist=load 差在哪

区别在分组粒度:load 按单个 test function 分发,loadgroupclasspytest.mark 分组后整体调度 —— 后者更适合有内部状态依赖的测试集。

立即学习Python免费学习笔记(深入)”;

  • 如果某 TestAPI class 里多个方法共用一个登录 Token,用 --dist=load 可能把 login 和 use_token 拆到不同进程,直接失败;换成 --dist=loadgroup 就能保序
  • --dist=loadgroup 需配合 @pytest.mark.xdist_group(name="api") 使用,否则默认按 class 名分组
  • 组太大(比如整个 TestIntegration 有 50 个方法)会导致某个 worker 负载过重,反而拖慢总耗时

多进程下 fixture 的初始化时机、日志混杂、覆盖率统计失效,这些都不是配置开关能一键解决的。得先看清测试本身有没有隐式共享状态——这才是并行真正的门槛。

text=ZqhQzanResources