Доступ к контейнеру запроса (event_dispatcher) в тестовом клиенте
Я создал простой тестовый пример в 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
имеет свой мир и игнорирует все внешнее.
Но тогда возникает вопрос, как я могу поместить своего слушателя в этот "мир"?
1 ответ
Я думаю, что отчасти проблема заключается в смешении стилей тестирования. У вас есть WebTestCase, который предназначен для очень высокого уровня тестирования (запросы и ответы). На самом деле он не должен заботиться о внутреннем устройстве, то есть о том, какие службы или слушатели вызываются. Его заботит только то, что при вводе x (ваш запрос) вы получите результат y (ваш ответ). Это позволяет гарантировать, что основные функциональные возможности, которые воспринимаются вашими пользователями, всегда выполняются, не беспокоясь о том, как это делается. Делаем эти тесты очень гибкими.
Изучая контейнер и сервисы, вы переходите на более низкий уровень тестирования, на котором тестируются взаимосвязанные сервисы. Обычно это делается только в рамках одного процесса по причинам, которые вы уже выяснили. Тест более высокого уровня имеет 2 отдельных жизненных цикла: один для самого теста, а другой для моделирования веб-запроса к вашему приложению, отсюда и разные идентификаторы объектов.
Решение состоит в том, чтобы передать что-то на более высокий уровень, например, установив заголовки или изменив вывод, чтобы вы могли проверить тело ответа. Вы также можете записать в какой-нибудь файл журнала и проверить журналы до / после запроса этого сообщения.
Другой вариант - переместить весь тест на более низкий уровень, где вам не нужны запросы и вместо этого вы будете работать только со службами. Для этого вы можете использовать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...
}