4

I want to test a service who send mail. I have create a unit test but i have some deprecation warning and i want to know the good use. In my setUp() function i get the service like this

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

But i have this message

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.

What is the best pratice to do ? I have the same message for security.authentication.manager

Jakub Zalas
  • 35,761
  • 9
  • 93
  • 125
eldiablo62
  • 305
  • 4
  • 17
  • I think the best practice would to use DI in your case. Have a look at this : https://symfony.com/doc/current/components/dependency_injection.html – Gabriel Deschamps Apr 20 '18 at 11:14

2 Answers2

4

Services were made private by default in Symfony 3.4.

Symfony 4.1

Starting with Symfony 4.1 all private services are made available in test environment via a special test container:

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

Symfony 3.4 and 4.0

One way you could solve it in Symfony 3.4 and 4.0 is to register a service locator in test environment, that would expose private services you need access to in tests.

Another way would be to simply create a public alias for each private service you need access to in tests.

For example:

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

In your test you'll be now able to access your private service via the public alias test_alias.swiftmailer.mailer:

$this->container = self::$kernel->getContainer();
$this->swiftMailer = $this->container->get('test_alias.swiftmailer.mailer');
Jakub Zalas
  • 35,761
  • 9
  • 93
  • 125
1

This approach with all its pros/cons is described in this post with code examples.


There is no need to extend and maintain extra configs lines for tests. There should be no public: true in them:

enter image description here

The best solution to access private services is to add a Compiler Pass that makes all services public for tests.

1. Update Kernel

 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. Require or create own Compiler Pass

Where PublicForTestsCompilerPass looks like:

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__');
    }
}

To use this class, just add the package by:

composer require symplify/package-builder

But of course, the better way is to use own class, that meets your needs (you might Behat for tests etc.).

Then all your tests will keep working as expected!

Let me know, how that works for you.

Tomas Votruba
  • 23,240
  • 9
  • 79
  • 115
  • Does not work with symfony 3.4 and twig : Fatal error: The service "templating.loader.cache" has a dependency on a non-existent service "templating.loader.wrapped". in C:\www\vendor\symfony\symfony\src\Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass.php:31 – Alex83690 Apr 04 '19 at 15:15
  • 2
    I see. In the end I'm using `public: true` to resolve all these problems. – Tomas Votruba Apr 04 '19 at 16:03