docteurklein/test-double-bundle

使用 symfony DIC 轻松创建测试替身

安装: 95,970

依赖: 0

建议: 0

安全: 0

星标: 41

关注者: 3

分支: 13

开放问题: 4

类型:symfony-bundle

1.0.0 2016-02-27 13:58 UTC

This package is auto-updated.

Last update: 2024-08-29 04:14:31 UTC


README

什么?

一个 symfony 扩展包,简化测试替身创建。

使用 DIC 标签,你可以自动用存根或伪造替换服务。

为什么?

为了提高测试的隔离性,增加测试用例的精确性和变化。

通常,我们的 behat 套件使用来自数据库存根的真实数据。
这迫使我们创建全局、通用、适用于所有情况的存根。

真实的数据库还意味着在每个场景之前重置状态。
这个过程很慢,只是为了解决隔离性问题的一个权宜之计。

一个理想的测试套件将使用内存存储库运行每个场景。
每个场景应定义在特定上下文下 SUS 的行为。
拥有全局隐式上下文(数据库存根)确实很难测试不同的情况。

一种解决方案是将你的存储库替换为存根。
每个场景仅配置所需的工作存根。

注意:存根数据在进程间不可持久,因此不适合像典型的 mink+behat 套件那样的端到端测试。

但是,现在存储库已经进行了双重配置,你怎么知道你的真实存储库是否仍然工作呢?
嗯,这就是基础设施测试的作用。只有它们才会针对真实的后端运行,无论是存储库的数据库,还是 http 客户端的服务器。

要访问真实的服务,只需使用 <original-id>.real

通过这样做,理论上你就有良好的覆盖率、隔离性、速度
并且可以更好地捕捉回归的源头。

同时应用 通过示例建模

如何?

安装

composer require docteurklein/test-double-bundle --dev

注册扩展包

    public function registerBundles()
    {
        $bundles = [
            new \DocteurKlein\TestDoubleBundle,
            // …
        ];

        return $bundles;
    }

注意:你可能只想在测试环境中添加此扩展包。

集成 behat

此方法与 Symfony2Extension 集成得很好。

然后你可以将服务及其/或预言注入到上下文类中。
你也可以注入容器并一次性访问所有服务。

示例

注意:以下示例使用 JmsDiExtraBundle 来简化代码。

存根

使用 prophecy 创建存根。

注意:如果你没有提供任何标签属性,则会创建一个存根。如果没有给 stub 属性提供类或接口,则将创建服务类的存根。存根类不能是最终的。

  • 首先,为服务定义一个存根 DIC 标签
/**
 * @Service("github_client")
 * @Tag("test_double", attributes={"stub"="GithubClient"})
 */
final class GuzzleClient implements GithubClient
{
    public function addIssue(Issue $issue)
    {
        // …
    }
}
  • 自动地,原始的 github_client 服务被替换为 github_client.stub 服务。

为了控制此存根,你必须使用 github_client.prophecy 服务

$issue = new Issue('test');
$container->get('github_client.prophecy')->addIssue($issue)->willReturn(true);

伪造

注意:伪造实际上是 DIC 别名。

假设你有一个想要双重配置的服务。

  • 首先,创建此服务并添加一个带有对应伪造服务的标签
/**
 * @Service("github_client")
 * @Tag("test_double", attributes={"fake"="github_client.fake"})
 */
final class GuzzleClient implements GithubClient
{
    public function addIssue(Issue $issue)
    {
        // …
    }
}
  • 然后,创建一个伪造实现并将其注册到伪造 ID
/**
 * @Service("github_client.fake")
 */
final class FakeClient implements GithubClient
{
    public function addIssue(Issue $issue)
    {
        // …
    }
}

Behat

注意:我们将 repo.invoiceshttp.client 标记为 存根

class Domain implements Context
{
    public function __construct($container)
    {
        $this->container = $container;
    }

    /**
     * @Given a building in "maintenance mode"
     */
    public function aBuildingInMaintenanceMode()
    {
        $this->building = new Building('BUILDING1337');
        $this->building->putInMaintenanceMode();
    }

    /**
     * @When its last unpaid invoice is being paid
     */
    public function itsLastUnpaidInvoiceIsBeingPaid()
    {
        $this->container
            ->get('repo.invoices.prophecy')
            ->findOneByReference('UNPAID04')
            ->willReturn(Invoice::ownedBy($this->building))
        ;
        $pay = $this->container->get('app.task.invoice.pay');
        $pay('UNPAID04');
    }

    /**
     * @Then it should be removed from maintenance mode
     */
    public function itShouldBeRemovedFromMaintenanceMode()
    {
        $this->container
            ->get('http.client.prophecy')
            ->removeFromMaintenanceMode('BUILDING1337')
            ->shouldHaveBeenCalled()
        ;

        $this->container->get('stub.prophet')->checkPredictions();
    }
}