Symfony 4:覆盖容器中的公共服务

Moh*_*ira 9 php symfony symfony4

我正在将项目迁移到Symfony4。在我的测试套件中,我们使用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);

仅用于测试,如何解决此问题?

谢谢

kal*_*osz 6

自 Symfony 3.3 起不推荐使用替换。您应该尝试使用别名而不是替换服务。 http://symfony.com/doc/current/service_container/alias_private.html

此外,您可以尝试这种方法:

$this->container->getDefinition('user.user_service')->setSynthetic(true); 在做之前 $container->set()

在 php 7.2 的测试中替换 Symfony 服务

  • @kallosz 应用程序用于发送邮件的服务不得在测试期间发送邮件。它必须由模拟代替。任何执行网络通信、在线支付或产生重负载(例如生成 pdf)的服务也必须如此。最后,还必须将服务替换为模拟以返回受控错误代码以检查应用程序是否正确处理错误。这是测试期间的正常行为。 (14认同)
  • 这是一个功能测试,这是一个正常的行为。 (4认同)

Moh*_*ira 4

最后,我找到了解决方案。也许不是最好的,但是,它正在工作:

services我创建了另一个测试容器类,并使用反射覆盖该属性:

<?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;
    }
Run Code Online (Sandbox Code Playgroud)

内核.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;
    }
Run Code Online (Sandbox Code Playgroud)

这确实很难看,但它确实有效。