Xdebug + PHPUnit 做覆盖率需满足四条件:启用 Xdebug coverage 模式、PHPUnit 显式配置 coverage、测试实际执行目标代码、正确配置 phpunit.xml 的 include/exclude 路径;否则报告为空或全 0%。

用 Xdebug + PHPUnit 做覆盖率,不是装上就能出报告
PHP 的代码覆盖率分析依赖运行时插桩(instrumentation),Xdebug 是最常用、兼容性最好的扩展。但仅启用 xdebug.mode=coverage 不够——PHPUnit 必须显式开启覆盖率收集,且测试需实际执行到目标代码行,否则报告里全是 0%。
常见错误现象:phpunit --coverage-html ./coverage 运行成功但生成的 HTML 报告为空,或所有文件显示 “No executable lines”;根本原因是未加载 Xdebug,或 PHP CLI 使用了不含 Xdebug 的配置。
- 确认 CLI 下 Xdebug 已启用:
php -m | grep xdebug,并检查php -i | grep "xdebug.mode"输出是否含coverage - PHPUnit 版本需 ≥ 9.3(推荐 10.x),低版本对 PHP 8+ 和 Xdebug 3 支持不完整
- 避免在
php.ini中全局开启xdebug.start_with_request=yes,它会拖慢所有 CLI 脚本;只在跑覆盖率时按需启用
phpunit.xml 配置里 coverage 部分不能只写路径
很多项目直接复制示例,在 <coverage></coverage> 下只配 <include></include>,结果覆盖率统计范围错乱:要么漏掉 src/ 下的类,要么把 tests/ 甚至 vendor/ 也塞进去算“被覆盖”。关键在于 <include></include> 定义的是“要统计哪些源码”,而 <exclude></exclude> 和 <report></report> 的结构决定最终输出逻辑。
典型有效配置片段:
立即学习“PHP免费学习笔记(深入)”;
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src/</directory>
</include>
<exclude>
<directory suffix=".php">src/Tests/</directory>
<file>src/Bootstrap.php</file>
</exclude>
<report>
<html outputDirectory="build/coverage"/>
</report>
</coverage>
-
processUncoveredFiles="true"让未被任何测试执行的文件也出现在报告中(显示 0%),否则它们直接消失 -
<exclude></exclude>必须显式排除测试辅助类,否则它们会被计入“被测代码”并拉低整体覆盖率 - 路径是相对于 phpunit.xml 所在目录的,不是相对于项目根或 src/ 目录
assertContains() 等断言不会提升行覆盖率,但会影响分支覆盖率
覆盖率分 line、function、class、branch 四种粒度,默认是 line。但很多人发现:明明写了 $this->assertContains('foo', $arr),对应那行 if 或 foreach 却还是黄色(部分覆盖)。这是因为 PHPUnit 的断言本身不改变被测代码执行流——真正影响分支覆盖率的是被测代码里的条件判断是否被不同取值触发。
- 例如被测函数有
if ($x && $y) { ... },只传$x=true, $y=true只覆盖了true && true分支;需额外用$x=false或$y=false补齐其他组合 -
assertContains()、assertEquals()等只验证结果,不参与被测代码逻辑分支;别指望靠多写几个 assert 来“刷高”覆盖率数字 - 想看分支详情,加参数
--coverage-clover build/clover.xml,再用工具(如 php-code-coverage 的 CLI)解析,或打开 HTML 报告里具体文件的“Branches”标签页
CI 环境下覆盖率失败常因权限或路径隔离导致
本地能跑出报告,CI(如 GitHub Actions、GitLab CI)里却报 Failed to write coverage report: failed to create directory 或生成空目录,本质是工作路径、用户权限、临时文件系统限制三者叠加的结果。
- 确保
build/coverage目录存在且可写:mkdir -p build/coverage && chmod 755 build/coverage - GitHub Actions 默认使用
runner用户,若 phpunit.xml 里写成绝对路径(如/tmp/coverage),可能因容器无写入权限失败 - 某些 CI 镜像默认禁用 Xdebug(出于性能考虑),需显式安装并启用:例如 Ubuntu 镜像中执行
sudo pecl install xdebug && echo "zend_extension=xdebug.so" | sudo tee /etc/php/*/cli/conf.d/99-xdebug.ini
覆盖率数值本身没意义,真正重要的是:哪些分支没走、哪些异常路径被忽略、哪些边界条件测试缺失——报告只是镜子,照出的是测试设计的盲区。



















