免责声明:我不是神圣实体。我所说的并不是绝对的真理。甚至不要害怕质疑世界,因为它可能是错的,而不是你。
今天对于任何人来说,自动化测试对于保持软件的质量和完整性的重要性都不是秘密,通常我们会谈论很多单元测试,但是今天,我们将更多地关注到 Symfony 框架中的集成测试。
好吧好吧!如果你没有耐心阅读这篇文章,我有一个测试项目,在下面的链接中实现了本文。
https://github.com/joubertredrat/symfony-testcontainers
如今,Symfony 框架是 PHP 领域最成熟、最稳定的框架之一,它拥有各种良好的实施解决方案,例如集成测试。但是,我个人一直认为,虽然集成测试本身很容易,但为测试提供外部依赖项并不总是那么容易,例如数据库。
即使使用像 Docker 这样的解决方案,我仍然意识到有必要以某种方式提供外部依赖项以进行测试,但是,存在一个非常有趣的解决方案,可以使这一步变得更加容易,即 Testcontainers。
Testcontainers 是一个开源框架,可让您以简单的方式提供使用 Docker 所需的任何外部依赖项,如数据库、消息代理、缓存系统或容器中的依赖项。
Testcontainers 相对于 Docker compose 或其他容器编排方式的一大优势是您可以对容器的配置进行编码,并且现在已经支持 Golang、Java、.NET、Node.js、Python、Rust 等各种语言其他语言,当然还有 PHP!
我第一次接触 Testcontainers 是在 Golang 的一个项目中,我非常喜欢配置 MongoDB 容器来进行存储库测试的功能,之后,我决定使用 Symfony 框架在我的个人 PHP 项目中做同样的事情。
Symfony 最大的优势之一是支持与 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 亲自查看。
就这样,下期见!
以上是Symfony 与 Testcontainers 的集成测试的详细内容。更多信息请关注PHP中文网其他相关文章!