问题
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
回答1:
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');
回答2:
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:
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.
来源:https://stackoverflow.com/questions/49938021/how-to-access-a-private-service-in-symfony-3-4-phpunit-tests