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

如何在多总线场景下用Behat测试Symfony Messenger事件分发?

看起来你遇到的问题核心在于Symfony的InMemoryTransport的get()方法并不是获取已发送的消息——这个方法是用来从队列中取出待处理的消息,而你的事件通过总线发送后,其实是存在Transport的sentMessages私有属性里的,所以调用get()自然拿不到内容。

下面是一步步的解决方案:

1. 自定义测试用的InMemoryTransport

原生的InMemoryTransport没有暴露已发送消息的接口,我们可以创建一个自定义子类来封装这个功能:

// src/Test/Transport/InMemoryTransport.php
declare(strict_types=1);

namespace App\Test\Transport;

use Symfony\Component\Messenger\Transport\InMemory\InMemoryTransport as BaseInMemoryTransport;

class InMemoryTransport extends BaseInMemoryTransport
{
    // 获取所有已发送的消息
    public function getSentMessages(): array
    {
        return $this->sentMessages;
    }

    // 清空已发送消息(避免测试场景间的数据污染)
    public function clearSentMessages(): void
    {
        $this->sentMessages = [];
    }
}

2. 配置测试环境使用自定义Transport

修改config/packages/test/messenger.yaml,让event传输使用我们自定义的类:

framework:
    messenger:
        default_bus: event.bus
        buses:
            command.bus:
                middleware:
                    - validation
            event.bus:
                default_middleware: allow_no_handlers
        transports:
            sync: 'sync://'
            event:
                dsn: 'in-memory:///'
                class: App\Test\Transport\InMemoryTransport # 指定自定义类
        routing:
            'App\AddMagazine': sync
            'App\MagazineAdded': event
            'App\EventAdapter': event

3. 修复MessengerContext的测试方法

更新你的MessengerContext,使用自定义Transport的方法来获取已发送消息,同时修正步骤参数的接收问题:

// src/Features/BehatContext/MessengerContext.php
declare(strict_types=1);

namespace App\Features\BehatContext;

use App\Test\Transport\InMemoryTransport;
use Assert\Assert;
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Symfony\Component\Messenger\Envelope;

class MessengerContext implements Context
{
    /**
     * @var InMemoryTransport
     */
    private $transport;

    public function __construct(InMemoryTransport $transport)
    {
        $this->transport = $transport;
    }

    // 每个场景前清空已发送消息,避免数据污染
    /**
     * @BeforeScenario
     */
    public function clearSentMessages(BeforeScenarioScope $scope): void
    {
        $this->transport->clearSentMessages();
    }

    /**
     * @Given /^(\d+) Events? "([^"]*)" is dispatched$/
     */
    public function assertEventIsDispatched(int $expectedCount, string $eventClassName): void
    {
        $sentMessages = $this->transport->getSentMessages();

        // 过滤出匹配的事件(因为你用了EventAdapter包装,需要取出内部的Event对象)
        $matchingEvents = array_filter($sentMessages, function (Envelope $envelope) use ($eventClassName) {
            $message = $envelope->getMessage();
            return $message instanceof \App\EventAdapter && $message->getEvent() instanceof $eventClassName;
        });

        Assert::assertEquals(
            $expectedCount,
            count($matchingEvents),
            sprintf("Expected %d instances of %s to be dispatched, found %d", $expectedCount, $eventClassName, count($matchingEvents))
        );
    }
}

额外检查点

  • 从你的日志来看,send_message中间件已经正常工作(消息已发送到InMemoryTransport),所以不需要额外配置总线中间件;
  • 可选优化:步骤正则可以改成/^(\d+) Events? "([^"]*)" (is|are) dispatched$/,让语法更严谨,不影响功能。

这样修改后,你就能正确获取到已发送的事件,完成Behat测试的断言了。

内容的提问来源于stack exchange,提问作者db306

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:22:26