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では、デフォルトでサービスがプライベートになります。今後のバージョン 3.4 ではget()、サービスがパブリックとしてマークされていない場合に、メソッドを使用してサービス コンテナからサービスをフェッチすると、非推奨の警告がトリガーされます。

これにより、すべてのサービスをコンストラクターの引数として受け取るパブリック サービスを作成せずに、このスモーク テストを実行し続ける方法があるかどうか疑問に思いました。これは、コンテナー内のほぼ 1000 のサービスが実行可能なオプションではありません。

4

3 に答える 3

1

このアプローチとそのすべての長所/短所については、この投稿でコード例とともに説明しています


プライベート サービスにアクセスする最善の方法は、すべてのサービスをテスト用に公開するコンパイラ パスを追加することです。

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 を移行します)。

その後、すべてのテストが期待どおりに機能し続けます。

于 2018-05-18T13:28:31.380 に答える
0

私は非常によく似たスモークテストを持っています (これにより、以前に問題を見つけることができました) - ただし、時間がかかる要素はありません。私の「プライベート」サービスのリストはどんどん長くなっており、何らかの形で->getContainer()->isPrivate($id)そうし続けるつもりはありません。

私が作成した、またはフレームワークからのパブリック サービスがまだいくつかあるので、表示されたら除外リストに追加します。

于 2017-10-11T18:08:37.573 に答える