首页 文章

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

提问于
浏览
4

从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 . 当服务尚未标记为公共时,使用 get() 方法从服务容器中获取服务时,即将发布的版本3.4将触发弃用警告 .

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

3 回答

  • 0

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


    访问私有服务的最佳解决方案是添加 a Compiler Pass that makes all services public for tests .

    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进行测试等) .

    Then all your tests will keep working as expected!

  • 1

    您可以为您的服务设置自定义配置,仅用于将所有内容设置为公开的测试环境 . 或者,您可以为要测试的服务(在测试环境中)添加别名 .

    问题是你将改变每个环境编译容器的方式,因此根据检索服务所需的时间长度可能不再有用 . 好消息是,开始时并不是特别有用,因为没有什么可以做到的,因为它很慢而且有opcache它不应该是一个问题 .

    对于进行烟雾测试以确保服务可用,使其在测试环境中公开是很好的(对我而言)或者您可以使用WebTestCase通过UI进行烟雾测试 . 通过确保您的路由可访问,您间接确保由于不可访问/配置错误的服务而导致500错误 .

    当涉及到来自容器的服务的功能测试时,我认为没有办法将它们公开或使它们混淆(仅在必要时进行测试) .

  • 0

    我有一个 very 类似的烟雾测试(这让我现在可以找到问题) - 但是没有时间元素 . 我的'private'服务列表已经变得越来越长,没有某种形式的 ->getContainer()->isPrivate($id) 将继续这样做 .

    我仍然会创建一些公共服务,或者来自框架,所以我很乐意将它们添加到排除列表中 .

相关问题