免責事項: 私は神聖な存在ではありません。私の言うことは絶対的な真実ではありません。世界に対してさえ疑問を抱くことを恐れないでください。あなたではなく、世界が間違っている可能性があるからです。
ソフトウェアの品質と整合性を維持するための自動テストの重要性は今日は誰にとっても秘密ではありません。通常は単体テストについてよく話しますが、今日は Symfony Framework への統合テストに重点を置きます。
わかったわかった!記事を読むのに忍耐力がない場合は、以下のリンクにこの記事を実装したテスト プロジェクトがあります。
https://github.com/joubertredrat/symfony-testcontainers
現在、Symfony Framework は PHP の世界で最も成熟し安定したフレームワークの 1 つであり、統合テストなどのさまざまな優れたソリューションが実装されています。しかし、個人的には、統合テスト自体は簡単でも、テスト用の外部依存関係を提供することは、たとえばデータベースのように必ずしも簡単ではないと常々思っていました。
Docker のようなソリューションを使用した場合でも、テスト用の方法で外部依存関係を提供する必要があることに気づきましたが、このステップをはるかに簡単にする非常に興味深いソリューションである Testcontainers が存在します。
Testcontainers は、Docker を使用して、データベース、メッセージ ブローカー、キャッシュ システム、またはコンテナ内の依存関係として必要な外部依存関係を簡単な方法で提供できるオープン ソース フレームワークです。
Docker compose やその他のコンテナ オーケストレーション方法に関連した Testcontainers の大きな利点は、コンテナのプロビジョニングをコーディングできることと、現在すでに Golang、Java、.NET、Node.js、Python、Rust などのさまざまな機能をサポートしていることです。他の言語、そしてもちろん PHP もです!
Testcontainers との最初の接触は Golang のプロジェクトであり、リポジトリ テストを実行するために MongoDB コンテナをプロビジョニングする機能がとても気に入ったので、その後、Symfony Framework を使用して PHP の個人プロジェクトで同じことを行うことにしました。
Symfony の最大の利点の 1 つは、PHPUnit と統合されたテストの実行、およびテストに必要なブートストラップを実行する機能的なカーネルをサポートしていることです。
Testcontainers は PHP をサポートしていますが、その実装は最近のものであり、https://github.com/testcontainers/testcontainers-php で確認できます。
以下に、このプロジェクトの外部依存関係である MySQL 8.0 コンテナの実装と、Symfony カーネルの起動、データベースとスキーマの作成を示します。
class IntegrationTestCase extends KernelTestCase { protected static ?MySQLContainer $container = null; public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); if (!static::$container) { static::$container = MySQLContainer::make('8.0', 'password'); static::$container->withPort('19306', '3306'); static::$container->run(); $kernel = self::bootKernel(); $container = $kernel->getContainer(); $application = new Application($kernel); $application->setAutoExit(false); $application->run( new ArrayInput(['command' => 'doctrine:database:create', '--if-not-exists' => true]) ); $entityManager = $container->get('doctrine')->getManager(); $metadata = $entityManager->getMetadataFactory()->getAllMetadata(); $schemaTool = new SchemaTool($entityManager); $schemaTool->dropSchema($metadata); $schemaTool->createSchema($metadata); } } public static function tearDownAfterClass(): void { parent::tearDownAfterClass(); if (static::$container instanceof MySQLContainer) { static::$container->remove(); } }
これにより、以下の例のように、テスト自体を実行するクラスの基本クラスが完成しました。
class UserRepositoryTest extends IntegrationTestCase { public function testSave(): void { $user = new User(); $user->setName('John Doe'); $user->setEmail('john@doe.local'); $repo = $this->getRepository(); $repo->save($user, true); self::assertNotNull($user->getId()); self::assertIsInt($user->getId()); self::assertTrue($user->getId() > 0); } public function testGetByEmail(): void { $user = new User(); $user->setName('John Doe'); $user->setEmail('john2@doe.local'); $repo = $this->getRepository(); $userNotFound = $repo->getByEmail($user->getEmail()); self::assertNull($userNotFound); $repo->save($user, true); $userFound = $repo->getByEmail($user->getEmail()); self::assertEquals($user->getEmail(), $userFound->getEmail()); } protected function tearDown(): void { parent::tearDown(); $connection = $this ->getContainer() ->get('doctrine') ->getManager() ->getConnection() ; $connection->executeStatement('TRUNCATE TABLE users'); } protected function getRepository(): UserRepository { return $this->getContainer()->get(UserRepository::class); } }
一連のテストを実行すると、モック動作のみを含むテストの単体テストと比較してテストの実行が遅くなることがわかりますが、このプロセス中に Testcontainers がテストで使用するために定義したコンテナをプロビジョニングしているため、これは正常です。 .
最後に、この機能を使用すると、100% のカバー率など、とんでもないことが可能になります。信じられないですか? https://joubertredrat.github.io/symfony-testcontainers でご自身の目で確認できます。
それでは、また会いましょう!
以上がTestcontainers を使用した Symfony での統合テストの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。