Symfony2在测试中访问私有服务

时间:2014-12-22 14:05:37

标签: php symfony dependency-injection phpunit private

目前,我正在测试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');

我希望将这些服务设置为PRIVATE,不会让我在测试中从容器中检索服务,但实际结果是:

$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"

这两个service_client服务之间的唯一区别是" lookup_service_client"注入了其他几项服务,而#34; payment_service_client"仅注入其他一项服务。

所以,问题是:

  1. 为什么我可以从容器" lookup_service_client"中检索,因为我已将其设置为私有?

  2. 为什么我可以检索" lookup_service_client",但无法检索" payment_service_client"因为上面只介绍了唯一的区别吗?

  3. 我可以访问私人服务是否是Symfony2错误?

3 个答案:

答案 0 :(得分:1)

Symfony 4.1中有一些新的变化:

  

在Symfony 4.1中,我们做了同样的事情,现在测试允许默认提取私有服务。

     

实际上,基于WebTestCase和KernelTestCase的测试现在可以通过$ client-> getContainer()或static :: $ container属性访问特殊容器,该属性允许获取未删除的私有服务。

您可以在news post中了解更多相关信息。

虽然这不是一个错误,但它肯定是反直觉的。 manual具体说:

  

现在该服务是私有的,您不应该获取该服务   直接来自容器:

     

$container->get('foo');

     

根据容器的优化方式,这可能有效,也可能无效   服务实例,即使在工作的情况下,也是如此   弃用。简单地说:如果你这样做,服务可以被标记为私人   不想直接从您的代码访问它。

这就是核心团队在Symfony 4中决定make this behavior more consistent and intuitive的原因:

  

在Symfony 3.2中不推荐使用Container :: set()方法设置或取消设置私有服务,4.0中不再支持;

     

使用Container :: has()检查是否存在私有服务将始终在Symfony 4.0中返回false;

     

在Symfony 3.2中不推荐使用Container :: get()方法请求私有服务,并且不再在4.0中返回该服务。

答案 1 :(得分:0)

2018和Symfony 3.4 / 4.0是此问题的最佳解决方案(目前为止)

此方法with all its pros/cons is described in this post with code examples

访问私有服务的最佳解决方案是添加编译器通行证,使所有服务公开以进行测试。而已。它在实践中看起来如何?

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

但是,当然,更好的方法是使用自己的类,满足您的需求(您可能需要测试等)。

然后您的所有测试都会按预期继续工作!

请告诉我,这对您有何帮助。

答案 2 :(得分:-1)

在容器中检查它们:

container:debug lookup_service_client

container:debug payment_service_client

在你的例子中,他们都有“FOO”类,也许就是这种情况