
本文详解如何正确配置 coverage.py,使其捕获 pytest 测试中通过 subprocess 或 multiprocessing 启动的 python 子进程的执行路径,解决“no data was collected”警告及覆盖率仅显示 __init__.py 的常见问题。
本文详解如何正确配置 coverage.py,使其捕获 pytest 测试中通过 subprocess 或 multiprocessing 启动的 python 子进程的执行路径,解决“no data was collected”警告及覆盖率仅显示 __init__.py 的常见问题。
在使用 pytest 进行集成测试时,若测试用例启动了独立的 Python 子进程(例如 GUI 应用、后台服务或 CLI 工具),默认的 coverage run 无法自动追踪这些进程的执行——因为 coverage 是进程隔离的。要覆盖子进程,必须启用 coverage.py 的子进程测量(subprocess measurement)机制,并确保配置精准生效。
✅ 正确配置步骤(关键修正)
你遇到的问题核心在于:COVERAGE_PROCESS_START=True 中的 True 不是布尔值,而是配置文件路径。官方文档明确指出:该环境变量的值将被用作 coverage 配置文件(如 .coveragerc 或 pyproject.toml)的路径。若设为 True,coverage 会尝试加载名为 True 的文件(不存在),导致子进程跳过初始化,最终只生成空数据或仅记录 __init__.py(因其由 import 触发,可能在主进程内完成)。
请按以下顺序修正:
-
确保 sitecustomize.py 正确安装且可被子进程导入
将以下内容保存为 sitecustomize.py(推荐放在项目根目录或虚拟环境 site-packages 下,确保子进程使用的 Python 解释器能 import 它):# sitecustomize.py import coverage coverage.process_startup()
⚠️ 注意:sitecustomize.py 必须位于子进程 Python 解释器的 sys.path 中(如虚拟环境的 lib/python3.x/site-packages/)。若子进程使用系统 Python 或不同环境,请同步部署该文件。
-
正确设置 COVERAGE_PROCESS_START 环境变量
不再设为 True,而是指向你的配置文件路径:export COVERAGE_PROCESS_START="$(pwd)/.coveragerc" # 或使用绝对路径(更可靠) export COVERAGE_PROCESS_START="/path/to/your/project/.coveragerc"
-
确认 .coveragerc 启用子进程支持并合理配置
在你的 .coveragerc 中,显式启用 subprocess 并保留原有设置:[run] source = src/ parallel = True relative_files = True omit = **/tests/* # ? 关键:启用子进程测量 include = src/** # 确保子进程中的源码路径被包含(比 omit 更优先) [paths] source = src/ */src/? 提示:[paths] 可帮助 coverage 在多进程合并时正确定位源文件路径,避免因工作目录差异导致文件匹配失败。
-
运行测试并合并数据
# 清理旧数据(可选) coverage erase # 运行测试(coverage 自动启动子进程测量) coverage run --rcfile=.coveragerc -m pytest tests/gui/test_screenshots.py # 合并所有并行数据文件(含子进程生成的 .coverage.* 文件) coverage combine # 生成报告 coverage report -m coverage html
? 常见陷阱与验证技巧
子进程未加载 sitecustomize.py?
在子进程入口脚本顶部添加 import sys; print(sys.path) 和 print("sitecustomize loaded")(在 process_startup() 调用后),确认其实际执行。仍提示 “No data was collected”?
检查 .coverage.* 文件是否生成(如你观察到的 .coverage.Ubuntu-22.5371.XcjyqXNx),若存在但 coverage combine 后无数据,说明路径不匹配:用 coverage debug sys 查看 config_files 和 data_file 路径;用 coverage debug subprocess 确认子进程是否检测到 COVERAGE_PROCESS_START。为何只有 __init__.py 有覆盖率?
因为 __init__.py 在主进程 import 时已被 coverage 记录,而子进程的实际业务代码未触发测量——根本原因仍是 COVERAGE_PROCESS_START 指向错误或 sitecustomize.py 未生效。
✅ 总结
子进程覆盖率不是“开箱即用”的功能,它依赖三个严格协同的组件:
① sitecustomize.py 中的 coverage.process_startup();
② COVERAGE_PROCESS_START 精确指向有效配置文件(非布尔值);
③ coverage combine 合并所有分散的数据文件。
只要确保这三点,即可完整捕获从 pytest 主进程到任意深度子进程的全链路执行路径,真正实现端到端的测试覆盖率洞察。










