mekras/symfony-bundle-testing

用于测试 Symfony 包的工具

安装: 116

依赖项: 0

建议者: 0

安全: 0

星标: 0

关注者: 1

分支: 0

开放问题: 0

类型:symfony-bundle

0.3.0 2024-07-04 17:09 UTC

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 标准的 ContainerBuilderTestContainerBuilder 类的新实例,它提供了以下描述的功能。

在一个测试中可以创建多个独立的容器。

加载容器扩展

此方法用于测试 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');
    // Ваши проверки…
}