Skip to content
Advertisement

Symfony 4 : Override public services in container

I am migrating our project to Symfony 4. In my test suites, we used PHPUnit for functional tests (I mean, we call endpoints and we check result). Often, we mock services to check different steps.

Since I migrated to Symfony 4, I am facing this issue: SymfonyComponentDependencyInjectionExceptionInvalidArgumentException: The "my.service" service is already initialized, you cannot replace it. when we redefine it like this : static::$container->set("my.service", $mock);

Only for tests, how can I fix this issue?

Thank you

Advertisement

Answer

Finally, I found a solution. Maybe not the best, but, it’s working:

I created another test container class and I override the services property using Reflection:

<?php

namespace MyBundleTest;

use SymfonyBundleFrameworkBundleTestTestContainer 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 SymfonyComponentHttpKernelKernel 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;
    }

It’s really ugly, but it’s working.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement