Symfony 4: переопределить публичные сервисы в контейнере

Я переношу наш проект на 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);

Только для тестов, как я могу исправить эту проблему?

Спасибо

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
9
0
8 084
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

У меня есть пара таких тестов (реальный код выполняет некоторые действия и возвращает результат, тестовая версия просто возвращает 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

$this->container->getDefinition('user.user_service')->setSyn‌​thetic(true); Я не могу использовать это в своих наборах тестов. Компилятор уже скомпилирован.
Mohammed Mehira 27.07.2018 10:27

если вы хотите заменить сервис в своем тестовом коде, это означает, что в вашем приложении что-то не так.

kallosz 27.07.2018 16:41

это функциональный тест, это нормальное поведение.

Mohammed Mehira 28.07.2018 17:14

@kallosz Служба, используемая приложением для отправки писем, не должна отправлять письма во время тестов. Его нужно заменить на макет. Так должны быть любые службы, выполняющие сетевое взаимодействие, онлайн-платежи или производящие большую нагрузку, такие как создание PDF-файлов. Наконец, услуга также должна быть заменена имитацией для возврата контролируемых кодов ошибок, чтобы проверить, правильно ли приложение обрабатывает ошибки. Это нормальное поведение во время тестов.

Frédéric Marchal 28.11.2018 10:40
Ответ принят как подходящий

Наконец, я нашел решение. Может быть, не самый лучший, но он работает:

Я создал еще один тестовый контейнерный класс и переопределил свойство 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 и выше вы можете опираться на специальный «тестовый» контейнер. Чтобы узнать, как его использовать, перейдите по следующим ссылкам:

Другие вопросы по теме