Pytest -n 2 没效果是因为未安装 pytest-xdist 插件或 fixture 作用域错误;默认 session 级 browser fixture 被多进程复用导致冲突,需改为 function/class 作用域并在 conftest.py 中重定义 context 和 page fixture。

Pytest 本身不支持并发,必须配合 pytest-xdist 才能真正并行跑 Playwright 测试;直接加 -n 参数但没装插件会静默退回到串行。
为什么 pytest -n 2 没效果?
Playwright 的 Python 官方插件 pytest-playwright 默认注册的是 session 级 fixture(如 browser、context),而 pytest-xdist 的每个 worker 是独立进程,共享 session fixture 会导致资源冲突或报错 Browser closed。不改 fixture 作用域就加 -n,实际仍是单进程顺序执行,只是多开了几个空进程占位。
- 验证方法:在测试里加
print(os.getpid()),你会发现所有用例输出的 PID 都一样 - 根本原因:默认
browserfixture 的scope="session",被 xdist 错误复用 - 正确做法:显式重定义 fixture,把 scope 改为
"function"或"class",并在 conftest.py 中覆盖官方 fixture
如何安全地让每个测试独享浏览器上下文?
Playwright 的并发安全边界在 BrowserContext —— 它天然支持多实例隔离,且比新建 Browser 开销小得多。你应该让每个测试函数拿到独立的 context 和 page,而不是复用全局 browser 实例。
- 在
conftest.py中重写contextfixture:@pytest.fixture def context(browser): return browser.new_context() - 再重写
pagefixture,确保它依赖context而非直接依赖browser:@pytest.fixture def page(context): return context.new_page() - 避免使用
browserfixture 在测试函数参数里——它属于进程级资源,不该暴露给用例
--browser 和 -n 组合时的实际行为
当你运行 pytest --browser chromium --browser firefox -n 2,xdist 不会自动帮你做“每浏览器开 2 个 worker”。它只会把全部测试用例(含不同 --browser 标记的)打散到 2 个 worker 进程里,而每个进程内部仍按 --browser 参数顺序串行执行 Chromium 和 Firefox 两套——结果是总耗时接近单浏览器串行的 2 倍,而非缩短。
立即学习“Python免费学习笔记(深入)”;
- 真要多浏览器+并发,得用
pytest-xdist的--boxed模式(每个 test 单独进程)或结合pytest.mark.parametrize+-n,例如:@pytest.mark.parametrize("browser_name", ["chromium", "firefox"]) def test_login(page, browser_name): # 用 browser_name 控制逻辑分支 pass -
--browser-channel(如chrome)和--browser(如chromium)不能混用,后者会覆盖前者 - CI 环境中注意:GitHub Actions 默认只允许 2 个并发 job,
-n 4不会起效
并发下最容易被忽略的泄漏点
Playwright 的 BrowserContext 和 Page 对象不会自动 GC,尤其在 xdist 多进程场景下,如果 fixture 没正确 yield 并 close(),worker 进程退出时可能残留未释放的 GPU 内存或 socket 连接,导致后续测试失败或机器负载飙升。
- 务必在 context fixture 中用
yield+context.close():@pytest.fixture def context(browser): ctx = browser.new_context() yield ctx ctx.close() # 这行不能少 - 不要在测试函数里手动调
page.close()或context.close(),这会破坏 fixture 生命周期 - 调试时加
PWDEBUG=1会强制开启 headed 模式并禁用超时,但它和-n冲突——只对第一个 worker 生效,其余 worker 仍 headless,容易误判问题是否复现


















