在测试客户端中访问请求容器 (event_dispatcher)

Accessing request container (event_dispatcher) within a test client

我在 Symfony 中创建了一个简单的测试用例。 因此,一个客户端应该监听将在请求期间分派的事件。 但是什么也没有发生,因为请求有自己的范围,或者我不知道为什么我无法访问其中的调度程序。

$this->client = static::createClient();
self::$container = $this->client->getContainer();

$dispatcher = self::$container->get('event_dispatcher');

$dispatcher->addListener('example', function ($event) {
    // Never executed
});

$this->client->request('POST', $endpoint, $this->getNextRequestParameters($i), [$file], $this->requestHeaders);
$this->client->getResponse();

永远不会调用侦听器。 当我稍微调试一下时,我发现通过 spl_object_hash($dispatcher) 的对象散列在最高级别与 request 级别不同。 如此看来request自有天地,无视外界的一切

但接下来的问题是如何让我的听众听到这个 "world"?

我认为部分问题在于测试风格的混合。您有一个 WebTestCase 用于非常高级别的测试(请求和响应)。它不应该真正关心内部结构,即调用了哪些服务或侦听器。它只关心给定输入 x(您的请求)您将获得输出 y(您的响应)。这允许确保始终满足用户感知的基本功能,而无需关心它是如何完成的。使这些测试非常灵活。

通过查看容器和服务,您将进入较低级别的测试,测试互连的服务。由于您已经发现的原因,这通常只在同一过程中完成。更高级别的测试有 2 个独立的生命周期,一个用于测试本身,一个用于对应用程序的模拟 Web 请求,因此有不同的 object 个 ID。

解决方案是向更高级别发出一些东西,例如通过设置 headers 或更改输出,您可以检查响应 body。您还可以写入一些日志文件并检查日志 before/after 对该消息的请求。

另一种选择是将整个测试移至较低级别,您不需要请求,而只使用服务。为此,您可以使用 KernelTestCase(而不是 WebTestCase),而不是调用 createClient(),而是调用 bootKernel。这将使您能够访问您的容器,您可以在其中修改 EventDispatcher。您可以直接调用代码,而不是发送请求,例如如果您只想测试侦听器,则分派一个事件,或者您可以使您的控制器作为服务可访问,然后手动创建请求,调用操作,然后检查响应或您想要断言的任何其他内容。这可能看起来大致像这样:

public function testActionFiresEvent()
{
    $kernel = static::bootKernel();

    $eventDispatcher = $kernel->getContainer()->get('event_dispatcher');

    // ...

    $request = Request::create();

    // This might not work when the controller
    // You can create a service configuration only used by tests,
    // e.g. "config/services_test.yaml" and provide the controller service there
    $controller = $kernel->getContainer()->get(MyController::class);

    $response = $controller->endpointAction($request);

    // ...Do assertions...
}