PAYATHON 2026

Laravel 12 中 php artisan test 结束后为何不触发 CommandFinished?

数据小陈

结论

CommandFinished 用于监听 Artisan 命令的执行生命周期,但不能可靠地判断 PHPUnit 测试套件是否已经结束。php artisan test 会启动测试运行器。要在测试套件结束时执行逻辑,应使用 PHPUnit 事件或测试运行器扩展。

Laravel 文档说明,应用测试期间默认不会分发 CommandStarting 和 CommandFinished 事件。如果测试代码本身调用 Artisan 命令,可以在对应的测试类中使用 WithConsoleEvents 启用这些事件。它处理的是测试内部的 Artisan 命令事件,不是 php artisan test 整次测试运行的结束钩子。Laravel Console Tests 文档

在整个测试套件结束后执行逻辑

使用 PHPUnit 时,可以注册测试运行器扩展,并订阅当前 PHPUnit 版本提供的测试运行结束事件。下面的示例使用 TestRunner\FinishedSubscriber。请先确认项目安装的 PHPUnit 版本包含这个接口和事件类,因为不同版本的事件 API 可能不同。

创建扩展类:

<?php

declare(strict_types=1);

namespace Tests\Extensions;

use PHPUnit\Runner\Extension\Extension;
use PHPUnit\Runner\Extension\Facade;
use PHPUnit\Runner\Extension\ParameterCollection;
use PHPUnit\TextUI\Configuration\Configuration;

final class AfterTestsExtension implements Extension
{
    public function bootstrap(
        Configuration $configuration,
        Facade $facade,
        ParameterCollection $parameters,
    ): void {
        $facade->registerSubscriber(new TestRunnerFinishedSubscriber());
    }
}

创建订阅者:

<?php

declare(strict_types=1);

namespace Tests\Extensions;

use PHPUnit\Event\TestRunner\Finished;
use PHPUnit\Event\TestRunner\FinishedSubscriber;

final class TestRunnerFinishedSubscriber implements FinishedSubscriber
{
    public function notify(Finished $event): void
    {
        // 在这里执行测试套件结束后的逻辑。
    }
}

在 phpunit.xml 的 <phpunit> 元素内注册扩展:

<extensions>
    <bootstrap class="Tests\Extensions\AfterTestsExtension" />
</extensions>

确认项目的 Composer 自动加载配置能够解析 Tests\Extensions,并确认实际运行测试时使用了这份 phpunit.xml。PHPUnit 扩展通过配置文件注册,订阅者接收相应的测试运行器事件。PHPUnit 扩展文档

排查要点

如果扩展类已加载,但回调没有执行,先检查当前 PHPUnit 版本使用的事件名称和订阅者接口。较新版本可能采用不同的测试运行结束事件。还要确认 php artisan test 没有通过参数切换到其他配置文件,并检查运行环境是否禁用了扩展。

如果只需要在 CI 流程的测试步骤结束后运行命令,可以把后续逻辑放在 CI 命令步骤中。这样不需要依赖 PHPUnit 的扩展 API。

备注:内容仅供参考。