You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHPUnit + Symfony:src目录外模块代码覆盖率为0%的问题

Symfony多模块项目代码覆盖率异常排查

问题描述

Symfony项目中,核心代码位于src/(命名空间App\),modules/下包含多个独立模块(各有自身composer.json与命名空间),所有测试均可正常执行,但通过PhpStorm或PHPUnit的--coverage-html生成覆盖率时,modules/下的模块代码覆盖率始终为0%。仅将模块代码移入App命名空间时覆盖率正常,但不符合项目架构需求。

项目结构

Project
 | composer.json
 | src/ (namespace App\)
 | tests/ (namespace App\Tests\)
 | modules/
 --| Airport/
   --| src/ (namespace Module\Airport\)
   --| tests/ (namespace Module\Airport\Tests\)
   --| composer.json
 --| OtherModule/
   --| src/ (namespace Module\OtherModule\)
   --| tests/ (namespace Module\OtherModule\Tests\)
   --| composer.json

PHPUnit配置

<?xml version="1.0" encoding="UTF-8"?>

<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         colors="true"
         bootstrap="tests/bootstrap.php"
>
    <php>
        <ini name="error_reporting" value="-1"/>
        <server name="APP_ENV" value="test" force="true"/>
        <env name="APP_ENV" value="test" force="true"/>
        <server name="SHELL_VERBOSITY" value="-1"/>
        <server name="SYMFONY_PHPUNIT_REMOVE" value=""/>
        <server name="SYMFONY_PHPUNIT_VERSION" value="9.5"/>
        <server name="KERNEL_CLASS" value="App\Kernel"/>
    </php>

    <testsuites>
        <testsuite name="Project Test Suite">
            <directory>tests</directory> <!-- Core tests -->
            <directory>modules/*/tests</directory> <!-- Multiple modules tests -->
        </testsuite>
    </testsuites>

    <coverage>
        <include>
            <directory suffix=".php">src</directory> <!-- Core coverage -->
            <directory suffix=".php">modules/*/src</directory> <!-- Multiple modules coverage -->
        </include>
    </coverage>

    <!--<listeners>
        <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener"/>
    </listeners>-->

    <extensions>
        <extension class="\DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension"/>
    </extensions>
</phpunit>

环境版本

  • PHPUnit 9.5.20
  • PHP 7.4.28
  • Xdebug 3.1.5

Xdebug配置

/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini,
xdebug
Support Xdebug on Patreon, GitHub, or as a business: https://xdebug.org/support
             Enabled Features (through 'xdebug.mode' setting)             
xdebug.auto_trace => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.cli_color => 0 => 0
xdebug.client_discovery_header => no value => no value
xdebug.client_host => 172.17.0.1 => 172.17.0.1
xdebug.client_port => 9003 => 9003
xdebug.cloud_id => no value => no value
xdebug.collect_assignments => Off => Off
xdebug.collect_includes => (setting removed in Xdebug 3) => (setting removed in Xdebug 3)
xdebug.collect_params => (setting removed in Xdebug 3) => (setting removed in Xdebug 3)
xdebug.collect_return => Off => Off
xdebug.collect_vars => (setting removed in Xdebug 3) => (setting removed in Xdebug 3)
xdebug.connect_timeout_ms => 200 => 200
xdebug.coverage_enable => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.default_enable => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.discover_client_host => Off => Off
xdebug.dump.COOKIE => no value => no value
xdebug.dump.ENV => no value => no value
xdebug.dump.FILES => no value => no value
xdebug.dump.GET => no value => no value
xdebug.dump.POST => no value => no value
xdebug.dump.REQUEST => no value => no value
xdebug.dump.SERVER => no value => no value
xdebug.dump.SESSION => no value => no value
xdebug.dump_globals => On => On
xdebug.dump_once => On => On
xdebug.dump_undefined => Off => Off
xdebug.file_link_format => no value => no value
xdebug.filename_format => no value => no value
xdebug.force_display_errors => Off => Off
xdebug.force_error_reporting => 0 => 0
xdebug.gc_stats_enable => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.gc_stats_output_dir => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.gc_stats_output_name => gcstats.%p => gcstats.%p
xdebug.halt_level => 0 => 0
xdebug.idekey => no value => no value
xdebug.log => no value => no value
xdebug.log_level => 0 => 0
xdebug.max_nesting_level => 256 => 256
xdebug.max_stack_frames => -1 => -1
xdebug.mode => develop,debug => develop,debug
xdebug.output_dir => /tmp => /tmp
xdebug.overload_var_dump => (setting removed in Xdebug 3) => (setting removed in Xdebug 3)
xdebug.profiler_append => Off => Off
xdebug.profiler_enable => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.profiler_enable_trigger => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.profiler_enable_trigger_value => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.profiler_output_dir => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.profiler_output_name => cachegrind.out.%p => cachegrind.out.%p
xdebug.remote_autostart => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_connect_back => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_enable => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_host => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_log => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_log_level => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_mode => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_port => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.remote_timeout => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.scream => Off => Off
xdebug.show_error_trace => Off => Off
xdebug.show_exception_trace => Off => Off
xdebug.show_local_vars => Off => Off
xdebug.show_mem_delta => (setting removed in Xdebug 3) => (setting removed in Xdebug 3)
xdebug.start_upon_error => default => default
xdebug.start_with_request => yes => yes
xdebug.trace_enable_trigger => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.trace_enable_trigger_value => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.trace_format => 0 => 0
xdebug.trace_options => 0 => 0
xdebug.trace_output_dir => (setting renamed in Xdebug 3) => (setting renamed in Xdebug 3)
xdebug.trace_output_name => trace.%c => trace.%c
xdebug.trigger_value => no value => no value
xdebug.use_compression => 0 => 0
xdebug.var_display_max_children => 128 => 128
xdebug.var_display_max_data => 512 => 512
xdebug.var_display_max_depth => 3 => 3

已尝试方案

  • 切换Xdebug和pcov两种覆盖率生成方式,结果一致
  • 修改phpunit.xml的<coverage>节点,添加modules/*/src目录
  • 调整测试命名空间、bootstrap.php位置
  • 验证bootstrap.php已引入vendor自动加载,autoload_static.php包含模块文件及命名空间(测试可正常执行)

排查方向与解决方案

1. Xdebug模式缺失coverage

当前Xdebug全局配置中xdebug.mode = develop,debug,未包含coverage模式。虽执行命令时添加了-dxdebug.mode=coverage,但建议修改全局配置确保覆盖:

xdebug.mode = develop,debug,coverage

2. PHPUnit路径通配符解析问题

尝试将模块目录明确列出,替代通配符写法:

<coverage>
    <include>
        <directory suffix=".php">src</directory>
        <directory suffix=".php">modules/Airport/src</directory>
        <directory suffix=".php">modules/OtherModule/src</directory>
    </include>
</coverage>

3. 模块命名空间映射错误

检查模块composer.json的autoload配置,确保命名空间与目录正确映射,例如Airport模块:

{
    "autoload": {
        "psr-4": {
            "Module\\Airport\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Module\\Airport\\Tests\\": "tests/"
        }
    }
}

执行composer dump-autoload更新自动加载规则。

4. Symfony Kernel加载限制

若模块包含独立Bundle,需在项目Kernel的registerBundles方法中加载对应Bundle,确保模块代码被框架实例化追踪。

5. 清理PHPUnit缓存

执行命令清理缓存后重新生成覆盖率:

php vendor/bin/phpunit --clear-cache

同时删除旧的覆盖率输出目录,避免缓存干扰。

内容的提问来源于stack exchange,提问作者Thomas Lavigne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 19:09:32