Symfony 4.4 控制器测试中Mock外部依赖服务的最优方案咨询
Symfony 4.4 控制器测试最优方案
方案一:直接在测试容器中替换Mock服务(无需重构现有代码)
你不需要手动实例化控制器,Symfony测试环境提供的容器支持直接替换现有服务,步骤如下:
- 启动KernelBrowser后,通过测试容器获取并替换
MyService为你构造的Mock对象 - 注意Mock的返回值要匹配控制器的调用逻辑:原控制器中把
getInfos返回值当作对象调用->infoId和->owners[0],你之前返回数组会报错,需要返回对应格式的对象
class WebhookControllerTest extends WebTestCase { use LoginTrait; public function testNewPayinIRessourceIdExists() { self::ensureKernelShutdown(); /** @var KernelBrowser $client */ $client = $this->startClient(); $client->enableProfiler(); // 构造Mock对象,返回格式匹配控制器预期的对象结构 $mockEvent = new \stdClass(); $mockEvent->infoId = 123; $mockEvent->owners = [456]; // 对应owners[0]的取值逻辑 $myService = $this->createMock(MyService::class); $myService->expects($this->once()) ->method("getInfos") ->with($this->equalTo(1111)) // 校验传入的RessourceId是否正确 ->willReturn($mockEvent); // Symfony 4.4 测试环境使用test.service_container获取可修改的测试容器 $container = $client->getContainer()->get('test.service_container'); // 替换容器中的MyService为Mock对象 $container->set(MyService::class, $myService); // 发起请求,此时控制器会自动注入你替换后的Mock服务 $client->request('GET', '/webhook/new/?RessourceId=1111'); // 断言响应状态码、内容,或者通过Profiler校验日志、邮件是否发送符合预期 $this->assertEquals(200, $client->getResponse()->getStatusCode()); } }
注意:如果你的Symfony版本没有默认开启测试容器的公共访问,需要在
config/packages/test/framework.yaml中添加配置:framework: test: service_container: public: true
方案二:重构代码优化可测试性(推荐长期维护的项目使用)
你提到的手动实例化控制器依赖太多的问题,根源是控制器承担了过多业务逻辑,不符合单一职责原则,建议做如下重构:
- 把webhook的业务处理逻辑(日志记录、查询事件、查询用户、发送邮件等)抽离为独立的应用服务,比如
WebhookEventHandler - 控制器仅保留HTTP层逻辑:接收请求参数、调用应用服务、返回响应,仅依赖
WebhookEventHandler一个服务
重构后代码示例:
// 抽离的独立服务 final class WebhookEventHandler { // 原控制器的5个依赖移到这里 public function __construct( private CustomLoggerService $customLogger, private EntityManagerInterface $entityManager, private MyService $myService, private UserMailer $userMailer, private AdminMailer $adminMailer ) {} public function handle(string $uri, ?string $ressourceId): void { // 把原控制器new方法里的所有业务逻辑移到这里 $this->customLogger->info("new event uri ".$uri); if($ressourceId) { $event = $this->myService->getInfos($ressourceId); // 其余业务逻辑不变 } else { $this->adminMailer->sendSimpleMessageToAdmin("no ressource id", "no ressource id"); } } } // 重构后的控制器 final class WebhookController extends AbstractController { public function __construct(private WebhookEventHandler $handler) {} /** * @Route("/webhook/new", name="webhook_new") */ public function new(Request $request): Response { $this->handler->handle( $request->getUri(), $request->query->get('RessourceId') ); return new JsonResponse(); } }
重构后测试成本大幅降低:
- 测试控制器时只需要Mock
WebhookEventHandler,校验它是否被正确调用即可 - 业务逻辑的测试可以脱离HTTP请求,直接对
WebhookEventHandler做单元测试,不需要启动内核,执行速度更快,依赖更清晰
内容的提问来源于stack exchange,提问作者MisterDebug
相关产品推荐
相关产品推荐

