如何在Symfony 3.4 PHPUnit测试中访问私有服务?

4

我希望测试一个发送邮件的服务。我已经创建了一个单元测试,但是出现了一些过时警告,我想知道正确的使用方法。在我的setUp()函数中,我像这样获取服务:

    $this->container = self::$kernel->getContainer();
    $this->swiftMailer = $this->container->get('swiftmailer.mailer');

但我收到了这条信息。
The "swiftmailer.mailer" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.

什么是最佳实践? 我有一个与security.authentication.manager相关的相同消息。

我认为在你的情况下最好的做法是使用 DI。请看这个链接:https://symfony.com/doc/current/components/dependency_injection.html - Gabriel Deschamps
2个回答

4

在Symfony 3.4中,默认情况下,服务被设置为私有

Symfony 4.1

从Symfony 4.1开始,所有私有服务都可以通过特殊的测试容器在测试环境中使用

class FooTest extends KernelTestCase
{
    static::bootKernel();
    $this->swiftmailer = static::$container->get('swiftmailer.mailer');
}

Symfony 3.4 和 4.0

在 Symfony 3.4 和 4.0 中,解决这个问题的一种方法是在测试环境中注册服务定位器,它会公开你需要在测试中访问的私有服务。

另一种方法 是为每个你需要在测试中访问的私有服务创建一个公共别名。

例如:

# app/config/config_test.yml
services:
    test_alias.swiftmailer.mailer:
        alias: '@swiftmailer.mailer'
        public: true

在您的测试中,您现在可以通过公共别名test_alias.swiftmailer.mailer访问您的私有服务:
$this->container = self::$kernel->getContainer();
$this->swiftMailer = $this->container->get('test_alias.swiftmailer.mailer');

1

这种方法的优缺点在这篇文章中用代码示例进行了描述


测试无需扩展和维护额外的配置行。它们中不应该有 public: true

enter image description here

最好的访问私有服务的解决方案是添加一个编译器通道,使所有服务对测试公开。

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. 需要或创建自己的编译器Pass

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

当然,更好的方法是使用符合您需求的自定义类(您可能需要Behat进行测试等)。 然后,所有测试都将按预期工作! 让我知道这对您是否有用。

无法与Symfony 3.4和Twig一起使用: 致命错误:服务“templating.loader.cache”依赖于不存在的服务“templating.loader.wrapped”。位于C:\ www \ vendor \ symfony \ symfony \ src \ Symfony \ Component \ DependencyInjection \ Compiler \ CheckExceptionOnInvalidReferenceBehaviorPass.php:31 - Alex83690
2
我明白了。最终,我使用“public: true”来解决所有这些问题。 - Tomas Votruba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接