在测试客户端中访问请求容器 (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...
}
我在 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...
}