Symfony2 доступ к частным сервисам в тестах

В настоящее время я работаю над тестированием некоторых сервисов в Symfony2 и пытаюсь использовать Guzzle MockPlugin для контроля ответов CURL. Symfony версия 2.3.8 используется. У меня интересное поведение, и я не уверен, что это ошибка Symfony2 или нет.

У меня есть эти услуги в services.yml:

lookup_service_client:
class: FOO
public: false
factory_service: lookup_client_builder
factory_method: build

lookup_repository_auth_type:
class: AuthType
arguments: ["@lookup_service_client"]

lookup_repository_cancel_reason:
class: CancelReason
arguments: ["@lookup_service_client"]

payment_service_client:
class: FOO
public: false
factory_service: payment_client_builder
factory_method: build

payment_repository:
class: Payment
arguments: ["@payment_service_client"]

Название классов не важно. Вы можете видеть, что «lookup_service_client» и «lookup_service_client» являются частными службами.

У меня есть тестовый класс, который расширяет Symfony \ Bundle \ FrameworkBundle \ Test \ WebTestCase. В одном тесте мне нужно сделать что-то вроде:

$lookup = $this->client->getContainer()->get('lookup_service_client');

$payment = $this->client->getContainer()->get('payment_service_client');

Я ожидал, что, установив эти сервисы как ЧАСТНЫЕ, я не позволю мне получать сервисы из контейнера в тестах, но реальный результат таков:

$lookup = $this->client->getContainer()->get('lookup_service_client'); => returns the service instance

$payment = $this->client->getContainer()->get('payment_service_client'); => returns an exception saying: "You have requested a non-existent service"

Единственное различие между этими службами tow service_client состоит в том, что lookup_service_client добавляется в несколько других служб, а payment_service_client добавляется только в одну другую службу.

Итак, вопросы:

  1. Почему я могу получить из контейнера «lookup_service_client», так как я установил его как частное?

  2. Почему я могу получить «lookup_service_client», но не могу получить «payment_service_client», так как единственное отличие представлено выше?

  3. Это ошибка Symfony2, что я могу получить доступ к частной службе?

4

Решение

В Symfony 4.1 произошли некоторые новые изменения:

В Symfony 4.1 мы делали то же самое, и теперь тесты позволяют получать частные сервисы по умолчанию.

На практике тесты, основанные на WebTestCase и KernelTestCase, теперь получают доступ к специальному контейнеру через $ client-> getContainer () или свойство static :: $ container, которое позволяет извлекать не удаленные частные сервисы.

Вы можете прочитать больше об этом в новость.

Хотя это не ошибка, это определенно противоречит интуитивно. руководство конкретно говорит:

Теперь, когда служба является частной, вы не должны получать службу
прямо из контейнера:

$container->get('foo');

Это может или не может работать, в зависимости от того, как контейнер был оптимизирован
инстанция службы и даже в тех случаях, когда она работает,
осуждается. Проще говоря: услуга может быть помечена как частная, если вы делаете
не хочу получать к нему доступ напрямую из вашего кода.

Именно поэтому основная команда решила сделать это поведение более последовательным и интуитивным в Symfony 4:

Установка или удаление частного сервиса с помощью метода Container :: set () устарела в Symfony 3.2 и больше не поддерживается в 4.0;

Проверка существования частного сервиса с помощью Container :: has () всегда возвращает false в Symfony 4.0;

Запрос частного сервиса с помощью метода Container :: get () устарел в Symfony 3.2 и больше не возвращает сервис в 4.0.

1

Другие решения

2018 и Symfony 3.4 / 4.0 — лучшее (пока что) решение этой проблемы


Этот подход со всеми его плюсами / минусами описано в этом посте с примерами кода.


Лучшее решение для доступа к частным услугам — добавить пропуск компилятора, который делает все сервисы общедоступными для тестов. Вот и все. Как это выглядит на практике?

1. Обновление ядра

 use Symfony\Component\HttpKernel\Kernel;
+use Symplify\PackageBuilder\DependencyInjection\CompilerPass\PublicForTestsCompilerPass;

final class AppKernel extends Kernel
{
protected function build(ContainerBuilder $containerBuilder): void
{
$containerBuilder->addCompilerPass('...');
+        $containerBuilder->addCompilerPass(new PublicForTestsCompilerPass());
}
}

2. Требуйте или создайте собственный пароль компилятора

куда PublicForTestsCompilerPass похоже:

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

final class PublicForTestsCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $containerBuilder): void
{
if (! $this->isPHPUnit()) {
return;
}

foreach ($containerBuilder->getDefinitions() as $definition) {
$definition->setPublic(true);
}

foreach ($containerBuilder->getAliases() as $definition) {
$definition->setPublic(true);
}
}

private function isPHPUnit(): bool
{
// defined by PHPUnit
return defined('PHPUNIT_COMPOSER_INSTALL') || defined('__PHPUNIT_PHAR__');
}
}

Чтобы использовать этот класс, просто добавьте пакет:

composer require symplify/package-builder

Но, конечно, лучше использовать собственный класс, который соответствует вашим потребностям (вы можете использовать Behat для тестов и т. Д.).

Тогда все ваши тесты будут работать как положено!

Дайте мне знать, как это работает для вас.

0

Проверьте их в контейнере:

container:debug lookup_service_client

container:debug payment_service_client

в вашем примере они оба имеют класс «FOO», может быть, это так

-1
По вопросам рекламы [email protected]