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

Symfony 7.2 Notifier组件异步消息测试失败排查求助

问题:NotifierInterface异步消息测试无法检测,ChatterInterface正常

生产环境中使用NotifierInterface发送异步消息功能正常,但测试环节无法检测到该服务发送的异步消息;而使用ChatterInterface发送消息的测试可正常通过。


测试不通过的场景(使用NotifierInterface)

<?php

namespace App\Controller;

// 省略必要命名空间

class HomeController extends AbstractController
{
    #[Route(path: '/')]
    public function home(
        NotifierInterface $notifier,
    ) {
        $subject = 'Symfony Notifier';
        $content = 'Message!';
        
        $notification = new Notification(
            subject: $subject,
            channels: [
                'chat/telegram',
            ],
        );
        $notification
            ->content($content)
            ->emoji('💕')
        ;
        
        $recipient = new Recipient(
            email: $adminEmail,
            phone: $adminPhone,
        );
        
        $notifier->send(
            $notification,
            $recipient,
        );
        
        return $this->render('home/index.html.twig');
    }
}

测试通过的场景(使用ChatterInterface)

<?php

namespace App\Controller;

// 省略必要命名空间

class HomeController extends AbstractController
{
    #[Route(path: '/')]
    public function home(
        ?ChatterInterface $chatter,
    ) {
        
        $telegramOptions = new TelegramOptions();

        $telegramMessage = (new ChatMessage('Hello, it is a new message'))
            ->transport('telegram')
            ->options($telegramOptions)
        ;

        if (null !== $chatter) {
            $chatter->send($telegramMessage);
        }
        
        return $this->render('home/index.html.twig');
    }
}

测试代码

<?php

namespace App\Tests\Application\Controller;

// 省略必要命名空间

class HomeControllerTest extends AbstractApplicationCase {
    
    public function testNotifierOneAsyncMessengeWasSent() {
        $client = static::createClient();
        $client->followRedirects(true);
        
        // 请求发送异步通知的控制器方法
        $crawler = $client->request('GET', '/');
        
        $event = $this->getNotifierEvent(); // 第一个场景返回null
        $notification = $this->getNotifierMessage(); // 第一个场景返回null
        
        //###> 断言 ###
        // $this->assertQueuedNotificationCount(1); // 和下方效果一致
        $this->assertQueuedNotificationCount(1, 'telegram');
        /** 第一个场景报错:
          * Failed asserting that the Transport telegram has queued "1" emails (0 queued).
          │
          │ C:\Workspace\SYMFONY\__SANDBOX__\drafts\vendor\symfony\framework-
          | bundle\Test\NotificationAssertionsTrait.php:32
          │ C:\Workspace\SYMFONY\__SANDBOX__\drafts\tests\Application\Controller\HomeControllerTest.php:29
          │ C:\Workspace\SYMFONY\__SANDBOX__\drafts\vendor\phpunit\phpunit\phpunit:104
         */

        if ($event instanceof MessageEvent) {
            $this->assertNotificationIsQueued($event);            
        }
        
        if ($notification instanceof MessageInterface) {
            $this->assertNotificationSubjectContains($notification, 'Hello');
            $this->assertNotificationTransportIsEqual($notification, 'telegram');
        }
        //###< 断言 ###
    }
}

配置文件

config/notifier.yaml

when@test: &test
    framework:
        notifier:
            chatter_transports:
                telegram:       'null://null'

framework:
    notifier:
        message_bus: messenger.default_bus
        
        chatter_transports:
            telegram:           '%env(TELEGRAM_DSN)%'

        channel_policy:
            urgent:
            -   'browser'
            
            high:
            -   'browser'
            
            medium:
            -   'browser'
            
            low:
            -   'browser'

        admin_recipients:
            -   email:                '%env(APP_ADMIN_EMAIL)%'
                phone:                '%env(APP_ADMIN_PHONE)%'

config/messenger.yaml

framework:
    messenger:
        transports:
            sync: 'sync://'
            
            async:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                options:
                    queue_name: async
                retry_strategy:
                    max_retries: 3
                    delay: 3000
                    multiplier: 2
                    max_delay: 10000

        routing:
            # NOTIFIER
            'Symfony\Component\Notifier\*': async

解决方案

1. 补全测试环境的Notifier配置

测试环境下的notifier.yaml仅配置了chatter_transports,缺少message_bus和与业务匹配的channel_policy,导致Notifier无法正确绑定消息总线、匹配传输通道。修改when@test块:

when@test: &test
    framework:
        notifier:
            message_bus: messenger.default_bus # 和生产环境一致绑定消息总线
            chatter_transports:
                telegram: 'null://null'
            channel_policy:
                # 确保所有优先级通道映射到代码中使用的chat/telegram
                urgent: ['chat/telegram']
                high: ['chat/telegram']
                medium: ['chat/telegram']
                low: ['chat/telegram']

代码中指定了channels: ['chat/telegram'],必须保证测试环境的channel_policy将对应优先级映射到该通道,否则Notifier找不到传输方式。

2. 改用Messenger断言检测异步消息

assertQueuedNotificationCount是针对同步发送的通知设计的,异步消息会被投递到Messenger队列,需要用Messenger的测试断言来检测:
在测试类中引入Symfony\Bundle\FrameworkBundle\Test\MessengerAssertionsTrait,替换原有断言逻辑:

// 检测队列中是否有1条消息
$this->assertMessageCount(1);

// 精确匹配Notifier的ChatMessage类
$this->assertQueuedMessageCount(\Symfony\Component\Notifier\Message\ChatMessage::class, 1);

// 验证消息内容
$messages = $this->getQueuedMessages();
/** @var \Symfony\Component\Notifier\Message\ChatMessage $message */
$message = $messages[0];
$this->assertEquals('Symfony Notifier', $message->getSubject());
$this->assertEquals('telegram', $message->getTransport());

3. 确认Messenger路由配置

确保测试环境下messenger.yaml的路由规则未被覆盖,Symfony\Component\Notifier\*必须路由到async传输。若测试环境有单独的Messenger配置,需保持与生产环境一致。

问题根源

  • ChatterInterface直接使用配置的传输通道同步发送,测试环境的null://null传输能被测试断言直接捕获;
  • NotifierInterface依赖message_bus和channel_policy完成消息路由,测试环境缺少这些配置,导致消息无法进入异步队列,或测试无法追踪到消息。

内容的提问来源于stack exchange,提问作者Григорий

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 16:58:16