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。
备注:内容仅供参考。
