mekras / symfony-bundle-testing
用于测试 Symfony 包的工具
Requires
- php: ^7.4|^8.0
- nyholm/psr7: ^1.5
- symfony/browser-kit: ^5.4|^6.0|^7.0
- symfony/framework-bundle: ^5.4|^6.0|^7.0
- symfony/yaml: ^5.3|^6.0
Requires (Dev)
- infection/infection: ^0.26
- phpstan/extension-installer: ^1.0
- phpstan/phpstan: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: 9.5.16
- symfony/psr-http-message-bridge: ^1.3|^2.0
This package is auto-updated.
Last update: 2024-09-04 17:29:30 UTC
README
这个库提供了用于自动化测试 Symfony 包(bundles)的工具。
目录
安装
要求
- PHP 7.4+
- Symfony 5.4+
通过 Composer 安装
在项目根目录的终端中执行以下命令
composer require --dev mekras/symfony-bundle-testing
集成测试
集成测试允许检查你的代码在与 Symfony 交互时将如何实际运行。
集成测试的主类
建议在每个项目中为所有集成测试类创建一个单独的主要类,作为测试配置和添加公共方法的单一入口点。
这个类应该继承自 BaseSymfonyIntegrationTestCase。
示例 - tests/Integration/IntegrationTestCase.php
namespace Acme\MyBundle\Tests\Integration; use Mekras\Symfony\BundleTesting\BaseSymfonyIntegrationTestCase; abstract class IntegrationTestCase extends BaseSymfonyIntegrationTestCase { }
所有集成测试类都应该继承自这个类或 BaseSymfonyIntegrationTestCase
。
定义你的包的主要类的名称
Symfony 包的集成测试的一个重要元素是了解它的主要类(*Bundle
)。这由 BaseSymfonyIntegrationTestCase::getBundleClass()
方法负责。如果你遵循 包命名约定,则 getBundleClass()
应该正确地确定并返回类名。如果它做不到这一点,你可以在自己的 IntegrationTestCase
中重写它
namespace Acme\MyBundle\Tests\Integration; use Acme\MyBundle\AcmeMyBundle; use Mekras\Symfony\BundleTesting\BaseSymfonyIntegrationTestCase; abstract class IntegrationTestCase extends BaseSymfonyIntegrationTestCase { protected function getBundleClass(): string { return MyBundle::class; } }
编写测试
测试骨架看起来像这样
namespace Acme\MyBundle\Tests\Integration; final class SomeTest extends SymfonyIntegrationTestCase { public function testSomething(): void { // Создаём тестовый контейнер зависимостей: $container = $this->createContainer(); // Здесь можно настраивать контейнер зависимостей и задавать ожидания. // Компилируем контейнер, чтобы подготовить его к тестам: $container->compile(); // Здесь можно писать утверждения. } }
通常,首先需要创建依赖项容器,因为几乎所有的 Symfony 集成都是通过它进行的。方法 $this->createContainer()
创建一个测试容器(它将在下面详细描述),它将成为你测试的基础。
容器使用最小必要的预配置创建
- 设置重要的
kernel.*
参数; - 注册由
getRequiredBundles()
方法返回的包(见下文); - 注册由上述
getBundleClass()
方法定义的测试包; - 执行其他一些操作。
在创建容器但 在编译之前,你可以进行额外的配置操作或设置期望,如下所述。
接下来,需要编译容器,以使其处于与应用程序运行时相对应的状态。这是通过方法 $container->compile()
完成的。
编译后,可以执行检查以验证容器。
以下问题将更详细地讨论。
测试依赖项容器
集成测试的核心是依赖项容器,它允许检查你的包扩展是否正确注册,以及它与 Symfony 核心和其他包的交互。
要创建测试容器,请使用方法 BaseSymfonyIntegrationTestCase::createContainer
final class SomeTest extends SymfonyIntegrationTestCase { public function testSomething(): void { $container = $this->createContainer(); // … } }
此方法将创建并返回一个扩展了 Symfony 标准的 ContainerBuilder
的 TestContainerBuilder 类的新实例,它提供了以下描述的功能。
在一个测试中可以创建多个独立的容器。
加载容器扩展
此方法用于测试 Symfony 容器扩展,并允许检查这些扩展是否正确加载以及是否正确配置了容器。
public function testSomething(): void { $container = $this->createContainer(); $container->loadExtension(new MyExtension()); $container->compile(); self::assertTrue($container->hasExtension('my_extension_alias')); }
加载其他包到容器中
public function testSomething(): void { $container = $this->createContainer(); $container->registerBundle(MonologBundle::class); $container->compile(); self::assertTrue($container->hasExtension('monolog')); }
如何检查容器中是否有需要的服务?
假设您的包需要在容器中注册以下服务标识符:some.service.id
和 щерук.service.id
。可以通过以下方式检查是否真的进行了注册:
public function testFooServiceExists(): void { $container = $this->createContainer(); $container->expectDefinitionsExists( 'some.service.id', 'other.service.id', // … ); $container->compile(); // Если не добавить эту строку, то PHPUnit может ругаться на отсутствие утверждений. $this->expectNotToPerformAssertions(); }
如何从容器中获取服务?
可以通过普通方式获取公共服务,即通过方法 Container::get()
public function testSomething(): void { $container = $this->createContainer(); $container->compile(); $someService = $container->get('some.service.id'); // Ваши проверки… }
可以通过定位器获取私有服务,或者将其设置为公开。
通过定位器获取
public function testSomething(): void { $container = $this->createContainer(); $this->addToLocator(['some.private.service.id'], $container); $container->compile(); $someService = $this->getFromLocator('some.private.service.id', $container); // Ваши проверки… }
设置为公开
public function testSomething(): void { $container = $this->createContainer(); $container->makePublic('some.private.service.id'); $container->compile(); $someService = $container->get('some.private.service.id'); // Ваши проверки… }
如何将其他包添加到容器中?
如果您想测试与其他包的集成,可以使用方法 BaseSymfonyIntegrationTestCase::getBundleClass()
将它们添加到测试容器中。
对于单个测试,您可以在创建和编译容器之间直接在测试中这样做。
public function testSomething(): void { $container = $this->createContainer(); // Добавляет в контейнер MonologBundle: $container->registerBundle(MonologBundle::class); $container->compile(); // Ваши проверки… }
如果需要为所有测试执行此操作,则建议在主类中重写方法 createContainer
。
namespace Acme\MyBundle\Tests\Integration; use Mekras\Symfony\BundleTesting\BaseSymfonyIntegrationTestCase; use Symfony\Bundle\MonologBundle\MonologBundle; use Symfony\Component\DependencyInjection\ContainerBuilder; abstract class IntegrationTestCase extends BaseSymfonyIntegrationTestCase { protected function createContainer(array $public = []): ContainerBuilder { $container = parent::createContainer($public); $container->registerBundle(MonologBundle::class); return $container; } }
同样,您可以立即通过重写方法 getRequiredBundles()
指定要加载的包列表。
namespace Acme\MyBundle\Tests\Integration; use Mekras\Symfony\BundleTesting\BaseSymfonyIntegrationTestCase; use Symfony\Bundle\MonologBundle\MonologBundle; use Symfony\Component\DependencyInjection\ContainerBuilder; abstract class IntegrationTestCase extends BaseSymfonyIntegrationTestCase { protected function getRequiredBundles(): array { return \array_merge( parent::getRequiredBundles(), [ MonologBundle::class, ] ); } }
如何解决“service or alias has been removed or inlined”错误?
如果您在从测试容器提取时遇到以下错误:
当容器编译时,"foo" 服务或别名已被删除或内联。您应该将其设置为公开,或者停止直接使用容器,改用依赖注入。
可以通过将其设置为公开来解决此问题,在 createContainer()
方法的参数 $public
中指定该服务。
public function testSomething(): void { $container = $this->createContainer(); $container->makePublic('some.private.service.id'); $container->compile(); $someService = $container->get('some.private.service.id'); // Ваши проверки… }