如何使用Symfony2和Doctrine回滚Behat 3功能测试中的提交?

时间:2015-10-28 23:59:39

标签: symfony doctrine-orm bdd behat

作为标题,我的目标是回滚在Behat功能测试期间做出的任何提交。 我检查了这个answer非常相似,但是它来自两年前,似乎无法做到。

现在有可能和Behat 3一起使用。

我知道使用PHPUnit我可以使用startUp和tearDown方法达到类似的效果。

我尝试使用@BeforeScenario和@AfterScenario注释来启动和回滚事务挂钩,但似乎那个和应用程序没有共享同一个实体管理器实例。

有什么建议吗?

谢谢。

更新

谢谢大家的意见。这里有一些新的考虑因素:

  • LOAD FIXTURES :是的,它有效。我可以在测试开始之前运行灯具,但问题(我的错误,不提它)是灯具有时需要几分钟才能加载,在测试开始前等待10分钟或更长时间是很烦人的。 / p>

  • BEGIN / ROLLBACK TRANSACTION :它也有效或似乎有效。我没有收到任何错误,但测试期间写入的数据在结束时仍在我的数据库中。我在标记为@BeforeScenario的方法中添加了第一个,后者在一个用@AfterScenario标记的方法中

$this->kernel->getContainer()
    ->get('doctrine.orm.entity_manager')
    ->getConnection()
    ->beginTransaction();

$this->kernel->getContainer()
   ->get('doctrine.orm.entity_manager')
   ->getConnection()
   ->rollBack();
  • SAVEPOINT :我认为这正是我需要的,但我的数据仍然存在。我尝试在@BeforeScenario方法中添加保存点的创建,并在@AfterScenario方法上添加回滚
public function gatherContexts(BeforeScenarioScope $scope) {
    $environment = $scope->getEnvironment();
    $connection = $this->kernel->getContainer()->get('doctrine.orm.entity_manager')->getConnection();
    $connection->beginTransaction();
    $connection->createSavepoint('tests');
}

public function rollback(AfterScenarioScope $scope) {
    $connection = $this->kernel->getContainer()->get('doctrine.orm.entity_manager')->getConnection();
    $connection->rollbackSavepoint('tests');
}

所有这些测试都用于测试我的API REST项目。经过这些考虑后,我认为Behat和我的应用程序并不共享实体管理器的相同实例。您是否能够在测试期间在测试和项目之间共享相同的实例?

4 个答案:

答案 0 :(得分:6)

如果您的上下文实现了KernelAwareContext,那么在@BeforeScenario和@AfterScenario注释方法中,您可以执行

$this->kernel->getContainer()->getDoctrine()->getConnection()->beginTransaction();
$this->kernel->getContainer()->getDoctrine()->getConnection()->rollBack();

假设您只有一个连接并且由em使用它。

您也可以尝试使用$connection->setRollbackOnly(),但请记住,它会依赖于您的基础数据库。如果你没想到它,Mysql可能会在很多情况下自动提交。

最后还有$connection->createSavepoint('savePointName')$connection->rollbackSavepoint('savePointName')

一起使用

这不在我的脑海中,因此可能需要进行一些调整。

答案 1 :(得分:1)

问题在于Behat使用了Browser-Kit中的客户端,因此您正遭受rebootable client的困扰。 幸运的是,Symfony2扩展从容器中获取客户端,因此我们可以覆盖它。这是对我有用的窍门:

创建一个包装器类:

class NoneRebootableClient extends Client
{
    public function __construct(KernelInterface $kernel, array $server = array(), History $history = null, CookieJar $cookieJar = null)
    {
        parent::__construct($kernel, $server, $history, $cookieJar);
        $this->disableReboot();
    }

    public function setServerParameters(array $parameters)
    {
        return;
    }
}

仅当您具有类似“在每种情况之前,设置身份验证标头”之类的内容时,才需要setServerParameters覆盖。当Behat在vendor/behatch/contexts/src/HttpCall/Request/BrowserKit.php:resetHttpHeaders中的每次调用之后重置标头时,我们将在第一次调用后松开auth标头。 注意:这可能会产生意想不到的副作用。

然后为test-env配置服务(test.client ID很重要,其behat用于客户端查找的ID):

test.client:
    class: App\Tests\NoneRebootableClient
    public: true
    arguments:
        - '@kernel'
        - []
        - '@test.client.history'
        - '@test.client.cookiejar'

然后在上下文中使用“之前/之后”方案,如上所述:

/**
 * @BeforeScenario
 */
public function startTransaction($event)
{
    $this->doctrine->getConnection()->beginTransaction();
}

/**
 * @AfterScenario
 */
public function rollbackTransaction($event)
{
    $this->doctrine->getConnection()->rollBack();
}

等等,我们是幂等的:)

答案 2 :(得分:0)

使用setUp方法,您可以开始交易。如果两个方法调用之间没有提交,则可以在tearDown方法中回滚它。

即使您回滚查询,在生产数据库上启动测试也非常危险。这是使用数据夹具初始化数据库测试的更好方法。如果你不能这样做(我想是这样),你想用生产数据进行测试。使用doctrine:migrations(或doctrine:schema:create)将生产数据库架构复制到您的开发环境中,并添加脚本以复制数据。

答案 3 :(得分:0)

我认为“删除”和“创建”架构是您正在寻找的解决方案。

我正在使用Behat3进行功能测试 - 我正在测试相当复杂的Web应用程序和REST API。我正在使用准备好的灯具,并在场景中添加额外的数据。

您可以设置Behat上下文来为每个(之前)场景加载灯具 - 这非常有用:

class CustomContext implements Context, KernelAwareContext {

    /**
     * @param type ScenarioEvent
     *
     * @BeforeScenario
     */
    public function reloadSchema($event)
    {
        // Note: EntityManager and ClassMetadata is required
        // reload Schema
        $schemaTool = new SchemaTool($entityManager);
        $schemaTool->dropSchema($metadata);
        $schemaTool->createSchema($metadata);
    }

    /**
     * @param type ScenarioEvent
     *
     * @AfterScenario
     */
    public function closeConnections($event)
    {
        // close connection(s)
    }

    // ...

}

在每个方案Doctrine2正在删除并创建架构之前。接下来感谢下面的捆绑包,您可以为您的场景加载特定/常用灯具。

我正在使用Behat灯具的以下配置:

多亏了这个你可以在场景中加载灯具:

Given the fixtures file "dummy.yml" is loaded
Given the fixtures file "dummy.yml" is loaded with the persister "doctrine.orm.entity_manager"
Given the following fixtures files are loaded:
  | fixtures1.yml |
  | fixtures2.yml |

如果您需要更多详细信息,请立即告诉我。