用 coverage.py parallel 与 combine 合并 pytest-xdist 覆盖率
问题场景
使用 pytest-xdist 的 -n auto 并行跑测试时,测试用例实际分布在多个 worker 进程中执行。若只对主 pytest 进程启动 coverage,worker 中执行的代码不会被记录,最终覆盖率会明显偏低,甚至只覆盖到收集阶段或少量主进程代码。
pytest-xdist 的 worker 是独立 Python 进程。coverage.py 的 parallel = True 只负责让每个启动了 coverage 的进程分别写数据文件,并不会自动让所有子进程都启动 coverage。因此需要解决两件事:
- 在每个 xdist worker 内启动 coverage;
- 用
coverage combine合并各 worker 生成的.coverage.*数据。
最小可运行示例
目录结构示例:
project/
.coveragerc
conftest.py
src/
calc.py
tests/
test_calc.py
1. 配置 .coveragerc
[run]
branch = True
source = src
parallel = True
[report]
show_missing = True
parallel = True 是关键:每个进程保存数据时使用独立文件名,避免互相覆盖。source = src 限定统计范围,实际项目可替换为包名或源码目录。
2. 在 xdist worker 内启动 coverage
在项目根目录放置 conftest.py:
import coverage
def pytest_configure(config):
if hasattr(config, 'workerinput'):
cov = coverage.Coverage(config_file='.coveragerc')
cov.start()
config._coverage_plugin = cov
def pytest_sessionfinish(session, exitstatus):
cov = getattr(session.config, '_coverage_plugin', None)
if cov is not None:
cov.stop()
cov.save()
这里只对带有 workerinput 属性的 xdist worker 启动 coverage。worker 结束时调用 save(),由于配置了 parallel = True,会生成类似 .coverage.hostname.pid.random 的文件。
3. 被测试代码与测试用例
src/calc.py:
def add(a, b):
return a + b
def is_even(n):
return n % 2 == 0
tests/test_calc.py:
from calc import add, is_even
def test_add():
assert add(1, 2) == 3
def test_even():
assert is_even(4) is True
assert is_even(5) is False
运行与验证
安装依赖并清理旧数据:
python -m pip install pytest pytest-xdist coverage
coverage erase
用 -n auto 并行执行测试,并通过 PYTHONPATH 让测试能导入 src 下的模块:
PYTHONPATH=src pytest -n auto
执行后应能在项目根目录看到多个 .coverage.* 文件。随后合并并生成报告:
coverage combine
coverage report -m
如果只执行 coverage report 而不先 coverage combine,在只存在并行数据文件时可能提示没有可报告数据。合并后报告中的覆盖率应包含各 worker 执行到的代码行,而不是只反映主进程。
边界与注意事项
parallel = True不是自动子进程插桩开关;没有在 worker 中启动 coverage 时,仍然只会得到主进程数据。- 不要同时混用 pytest-cov 的
--cov和本文的手动启动逻辑,否则可能产生重复统计或数据文件冲突。若项目已使用 pytest-cov,应确认其 xdist 合并行为并只保留一种方案。 - 如果测试还会启动普通子进程、多进程池或命令行程序,需要额外使用
COVERAGE_PROCESS_START配合sitecustomize.py等方式让这些进程自动启动 coverage,并继续使用coverage combine合并。 - worker 异常退出时可能来不及保存数据;需要关注测试进程是否正常结束。
source路径应按项目实际包结构设置,避免把测试文件或虚拟环境代码纳入统计。- 覆盖率数据只说明哪些代码被执行,不等同于测试质量;合并后仍应结合分支覆盖和缺失行进行人工检查。
- 点赞
- 收藏
- 关注作者
评论(0)