Avertissement : je ne suis pas une entité divine. Ce que je dis n'est pas une vérité absolue. N'ayez pas peur de remettre en question même le monde, car il se peut qu'il ait tort, pas vous.
Aujourd'hui, l'importance des tests automatisés pour maintenir la qualité et l'intégrité de vos logiciels n'est un secret pour personne et normalement nous parlons beaucoup de tests unitaires, mais, aujourd'hui, nous nous concentrerons davantage sur les tests d'intégration dans Symfony Framework.
D'accord, d'accord ! Si vous n'avez pas la patience de lire l'article, j'ai un projet test avec la mise en œuvre de cet article dans le lien ci-dessous.
https://github.com/joubertredrat/symfony-testcontainers
Aujourd'hui, Symfony Framework est l'un des frameworks les plus matures et les plus stables de l'univers PHP et il propose diverses solutions bien implémentées, comme des tests d'intégration par exemple. Mais, personnellement, j'ai toujours pensé que même s'il était facile de faire des tests d'intégration soi-même, fournir des dépendances externes pour les tests n'a pas toujours été aussi simple, comme les bases de données par exemple.
Même avec une solution comme Docker, j'ai quand même réalisé la nécessité de fournir les dépendances externes d'une certaine manière pour les tests, mais il existe une solution très intéressante qui peut rendre cette étape beaucoup plus facile, les Testcontainers.
Testcontainers est un framework open source qui vous permet de fournir de manière simple toute dépendance externe dont vous avez besoin à l'aide de Docker, comme bases de données, courtier de messages, système de cache ou toute dépendance dans un conteneur.
Le gros avantage de Testcontainers par rapport à Docker Compose ou à d'autres méthodes d'orchestration de conteneurs est que vous pouvez coder le provisionnement du conteneur et qu'aujourd'hui, vous bénéficiez déjà du support de Golang, Java, .NET, Node.js, Python, Rust, divers d'autres langages et bien sûr, PHP aussi !
Mon premier contact avec Testcontainers était dans un projet en Golang et j'ai tellement aimé la possibilité de provisionner le conteneur MongoDB pour faire les tests du référentiel et après cela, j'ai décidé de faire la même chose dans mon projet personnel en PHP en utilisant Symfony Framework.
L'un des plus grands avantages de Symfony est la prise en charge des tests intégrés à PHPUnit et avec un noyau fonctionnel pour effectuer le bootstrap nécessaire aux tests.
Bien que Testcontainers prenne en charge PHP, leur implémentation est récente et vous pouvez la consulter sur https://github.com/testcontainers/testcontainers-php.
Ci-dessous nous avons une implémentation du conteneur MySQL 8.0, qui est une dépendance externe de ce projet, et le démarrage du noyau Symfony, la création de la base de données et du schéma.
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(); } }
Avec cela, nous avons la classe de base pour les classes qui exécuteront elles-mêmes les tests, comme exemple ci-dessous.
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); } }
Lors de l'exécution d'une suite de tests, vous pouvez constater que les tests s'exécuteront lentement par rapport aux tests unitaires de tests avec simplement des comportements simulés, mais cela est normal, car pendant ce processus, Testcontainers provisionne le conteneur que vous avez défini pour l'utiliser dans les tests. .
Enfin, avec cette installation, il est possible de faire des choses folles, comme une couverture à 100%. Vous n'y croyez pas ? Vous pouvez le constater par vous-même sur https://joubertredrat.github.io/symfony-testcontainers.
Ça y est, à la prochaine !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!