Symfony命令单元测试应继承TestCase,手动实例化命令并注入Mock依赖,用CommandTester执行并断言状态码、输出及异常,覆盖正常流程、参数缺失、无效值、依赖失败和空数据等场景。

Symfony 命令的单元测试,核心不是模拟终端输入或检查控制台输出,而是验证命令的业务逻辑是否按预期执行——比如是否调用了正确的服务、是否正确处理了参数、是否在特定条件下抛出异常。它不启动内核、不访问数据库、不依赖真实输入流,靠隔离 + Mock + 断言驱动。
选对基类:用 TestCase,不是 WebTestCase 或 KernelTestCase
命令本质是普通 PHP 类(继承 Command),其核心逻辑(如 doExecute 方法中的服务调用、数据处理)应可脱离运行时环境被验证。因此:
- 继承 PHPUnit\Framework\TestCase,不是
WebTestCase或KernelTestCase - 禁止调用
self::bootKernel()、static::createClient()或任何容器相关方法 - 避免读取真实配置、连接数据库、发送 HTTP 请求
手动实例化命令 + 注入 Mock 依赖
命令通常依赖其他服务(如 MailerInterface、EntityManager、LoggerInterface)。测试时需用 PHPUnit 的 Mock 替换它们:
- 创建 Mock:
$mailer = $this->createMock(MailerInterface::class) - 设定行为:
$mailer->expects($this->once())->method('send') - 构造命令:
$command = new SendNewsletterCommand($mailer, $logger) - 若命令构造器有必填参数,全部传入;若使用 setter 注入,调用对应 setter
模拟输入与执行:绕过 Console 组件胶水层
不走 $client->request() 那套,也不启动真实 Application。直接调用命令逻辑入口:
- 调用
$command->run($input, $output),但需先构造轻量InputInterface和OutputInterface - 推荐用
Symfony\Component\Console\Tester\CommandTester封装:
$tester = new CommandTester($command);
$tester->execute(['--limit' => 10, 'env' => 'prod']);
$this->assertSame(0, $tester->getStatusCode());
$output = $tester->getDisplay();
$this->assertStringContainsString('Processed 10 items', $output);
-
CommandTester拦截输出、捕获退出码、屏蔽交互提示,完全可控 - 参数以关联数组传入,键为选项名(
--limit)、值为实际值;命令名作为位置参数(如['cache:clear'])
覆盖关键场景:参数、异常、边界值
一个高质量的命令测试套件需包含:
- 正常流程:传入合法参数,断言返回码为 0,输出含预期文本
-
缺失必填参数:触发
InvalidArgument异常,用$this->expectException(InvalidArgumentException::class) - 无效选项值:如传入负数 limit,断言抛出自定义异常或返回非零码
- 依赖失败路径:Mock 服务抛出异常,验证命令是否优雅降级(如记录错误、返回 1)
- 空数据处理:查询无结果时,是否跳过后续操作、是否输出提示信息

















