在SF3.4及更高版本中运行Symfony DIC烟雾测试

从DI容器中获取服务是我的测试套件中烟雾测试的一个组成部分.例如,以下测试确保在容器中注册的服务的构造没有问题,并且这些服务不需要花费太多时间来构建.

private const DEFAULT_TRESHOLD = 30;

public function testServicesLoadInTime()
{
    $client = static::createClient();

    /**
     * Add serviceid as key, possible values:
     * - false: Skip test for this service
     * - integer value: Custom responsetime
     */
    $customCriteria = [
        // See: https://github.com/symfony/monolog-bundle/issues/192
        'monolog.activation_strategy.not_found' => false,
        'monolog.handler.fingers_crossed.error_level_activation_strategy' => false,
        // Should not be used directly (Factories will inject other parameters)
        'liip_imagine.binary.loader.prototype.filesystem' => false,
        // Services that are allowed to load longer (Only for CLI tasks like workers)
        'assetic.asset_manager' => 1000,
    ];

    foreach ($client->getContainer()->getServiceIds() as $id) {
        if (isset($customCriteria[$id]) && $customCriteria[$id] === false) {
            continue;
        }
        try {
            $startedAt = microtime(true);
            $service = $client->getContainer()->get($id);
            $elapsed = (microtime(true) - $startedAt) * 1000;
            $this->assertNotNull($service);
            $treshold = $customCriteria[$id] ?? self::DEFAULT_TRESHOLD;
            $this->assertLessThan($treshold, $elapsed, sprintf(
                'Service %s loaded in %d ms which is more than the %d ms threshold',
                $id, $elapsed, $treshold
            ));
        } catch (InactiveScopeException $e) {
            // Noop
        } catch (\Throwable $ex) {
            $this->fail(sprintf("Fetching service %s failed: %s", $id, $ex->getMessage()));
        }
    }
}

然而. Symfony的第4版将生成services private by default.当服务尚未标记为public时,使用get()方法从服务容器获取服务时,即将发布的版本3.4将触发弃用警告.

这让我想知道是否有办法让这个冒烟测试运行而不创建一个公共服务,它将所有服务作为构造函数参数,当然容器中的近1000个服务当然不是一个可行的选择.

最佳答案 这种方法
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

但是,当然,更好的方法是使用自己的类,满足您的需求(您可以迁移Behat进行测试等).

那么你所有的测试都会按预期继续工作!

点赞