用 coverage.py parallel 与 combine 合并 pytest-xdist 覆盖率

举报
yd_229231784 发表于 2026/11/14 10:08:14 2026/11/14
【摘要】 并行 pytest 下各 xdist worker 是独立进程,默认只统计主进程会让覆盖率偏低。本文用 .coveragerc 的 parallel=true 配合 conftest 在 worker 内启动 coverage,再用 coverage combine 合并并报告,给出最小可运行验证步骤与边界说明。

问题场景

使用 pytest-xdist 的 -n auto 并行跑测试时,测试用例实际分布在多个 worker 进程中执行。若只对主 pytest 进程启动 coverage,worker 中执行的代码不会被记录,最终覆盖率会明显偏低,甚至只覆盖到收集阶段或少量主进程代码。

pytest-xdist 的 worker 是独立 Python 进程。coverage.py 的 parallel = True 只负责让每个启动了 coverage 的进程分别写数据文件,并不会自动让所有子进程都启动 coverage。因此需要解决两件事:

  1. 在每个 xdist worker 内启动 coverage;
  2. 用 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 路径应按项目实际包结构设置,避免把测试文件或虚拟环境代码纳入统计。
  • 覆盖率数据只说明哪些代码被执行,不等同于测试质量;合并后仍应结合分支覆盖和缺失行进行人工检查。
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。