Я переношу наш проект на Symfony 4. В моих тестовых наборах мы использовали PHPUnit для функциональных тестов (я имею в виду, мы вызываем конечные точки и проверяем результат). Часто мы имитируем сервисы, чтобы проверить разные этапы.
Поскольку я перешел на Symfony 4, я столкнулся с этой проблемой: Symfony\Component\DependencyInjection\Exception\InvalidArgumentException: The "my.service" service is already initialized, you cannot replace it.
когда мы переопределяем его так: static::$container->set("my.service", $mock);
Только для тестов, как я могу исправить эту проблему?
Спасибо






У меня есть пара таких тестов (реальный код выполняет некоторые действия и возвращает результат, тестовая версия просто возвращает false для каждого ответа).
Если вы создаете и используете настраиваемую конфигурацию для каждой среды (например: services_test.yaml или в Symfony4, вероятно, tests / services.yaml) и сначала включаете dev / services.yaml, но затем переопределяете службу, которую хотите, то будет использовано последнее определение.
приложение / config / services_test.yml:
imports:
- { resource: services.yml }
App\BotDetector\BotDetectable: '@App\BotDetector\BotDetectorNeverBot'
# in the top-level 'live/prod' config this would be
# App\BotDetector\BotDetectable: '@App\BotDetector\BotDetector'
Здесь я использую интерфейс в качестве имени службы, но он будет делать то же самое и со стилем @ service.name.
Замена не рекомендуется, начиная с Symfony 3.3. Вместо замены службы вам следует попробовать использовать псевдонимы. http://symfony.com/doc/current/service_container/alias_private.html
Также вы можете попробовать такой подход:
$this->container->getDefinition('user.user_service')->setSynthetic(true);
прежде чем делать $container->set()
Заменить сервис Symfony в тестах на php 7.2
если вы хотите заменить сервис в своем тестовом коде, это означает, что в вашем приложении что-то не так.
это функциональный тест, это нормальное поведение.
@kallosz Служба, используемая приложением для отправки писем, не должна отправлять письма во время тестов. Его нужно заменить на макет. Так должны быть любые службы, выполняющие сетевое взаимодействие, онлайн-платежи или производящие большую нагрузку, такие как создание PDF-файлов. Наконец, услуга также должна быть заменена имитацией для возврата контролируемых кодов ошибок, чтобы проверить, правильно ли приложение обрабатывает ошибки. Это нормальное поведение во время тестов.
Наконец, я нашел решение. Может быть, не самый лучший, но он работает:
Я создал еще один тестовый контейнерный класс и переопределил свойство services, используя Reflection:
<?php
namespace My\Bundle\Test;
use Symfony\Bundle\FrameworkBundle\Test\TestContainer as BaseTestContainer;
class TestContainer extends BaseTestContainer
{
private $publicContainer;
public function set($id, $service)
{
$r = new \ReflectionObject($this->publicContainer);
$p = $r->getProperty('services');
$p->setAccessible(true);
$services = $p->getValue($this->publicContainer);
$services[$id] = $service;
$p->setValue($this->publicContainer, $services);
}
public function setPublicContainer($container)
{
$this->publicContainer = $container;
}
Kernel.php:
<?php
namespace App;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
public function getOriginalContainer()
{
if (!$this->container) {
parent::boot();
}
/** @var Container $container */
return $this->container;
}
public function getContainer()
{
if ($this->environment == 'prod') {
return parent::getContainer();
}
/** @var Container $container */
$container = $this->getOriginalContainer();
$testContainer = $container->get('my.test.service_container');
$testContainer->setPublicContainer($container);
return $testContainer;
}
Это действительно некрасиво, но работает.
Если вы используете Symfony 3.4 и ниже, вы можете переопределить сервисы в контейнере, независимо от того, приватный он или публичный. Будет отправлено только уведомление об ограничении стоимости, содержание которого похоже на сообщение об ошибке из вопроса.
На Symfony 4.0 ошибка из вопроса была показана.
Но на Symfony 4.1 и выше вы можете опираться на специальный «тестовый» контейнер. Чтобы узнать, как его использовать, перейдите по следующим ссылкам:
$this->container->getDefinition('user.user_service')->setSynthetic(true);Я не могу использовать это в своих наборах тестов. Компилятор уже скомпилирован.