使用Behat 3,Doctrine 2和Symfony 2测试数据库隔离

时间:2015-07-21 18:53:20

标签: symfony testing doctrine-orm functional-testing behat

我在Symfony2 / Doctrine2应用程序上引入功能性behat测试,并决定如何处理数据库隔离和数据夹具。

使用自己完全独立的mysql数据库建立一个单独的测试环境是否存在任何陷阱,该数据库在执行behat测试套件之前由转储导入填充,然后在套件执行后清空?我正在努力避免使用数据夹具,除非我真的需要这样以节省手动编写外键关系的时间而不是。

感谢任何指导。

4 个答案:

答案 0 :(得分:4)

就Symfony / Doctrine / Behat而言,如果你想成为那些遵循最佳实践的人之一:

  1. 你应该隔离你的环境dev,test,prod,stag ... Setting up isolated environments as a symfony application base 最多可以构建文件夹结构标题。
  2. 您应该隔离测试数据库并使用sqlite而不是 MySQL用于性能目的。 Using multiple SQLite entity managers for multiple bundles and databases in test environment, 你只能使用一个。
  3. 你应该使用灯具给自己灵活性并摆脱 手工处理的负担。不要试图避免使用它们! Creating doctrine data fixtures in symfony
  4. 等等.....只需检查我经常发布的帖子in this site 读我自己。
  5. Behat 3 composer entries and the behat.yml

答案 1 :(得分:1)

我们目前有一个单独的测试数据库,并使用两个灯具和预填充数据库的组合。

预填充数据库包含几乎所有测试中都需要存在的最少信息。我们曾经使用灯具这样做,但它太慢了,所以现在我们这样填充DB:

if Rails.env.production?

然后,我们为Alfonso描述的每个测试用例加载特定的灯具。

我们使用MYSQL进行测试,因为根据我们的经验,瓶颈不是数据库,而是学说的元数据缓存。如果在redis中设置元数据缓存,则测试速度会急剧增加。

答案 2 :(得分:1)

为了回应@ madness-method并完成以下答案:

  

预填充数据库包含所需的最少信息   几乎所有的测试都会出现。我们曾经用夹具做这件事   但它太慢了,所以现在我们这样填充数据库:

sigprocmask(SIG_SETMASK, &oldmask, NULL)
     

然后,我们为每个测试用例加载特定的灯具   阿方索描述道。

     

我们使用MYSQL进行测试,因为根据我们的经验,瓶颈不是   DB但是学说的元数据缓存。如果设置元数据缓存   在redis中,测试的速度急剧增加。

您应该使用:

#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

static void sig_quit(int signo) {
    printf("caught SIGQUIT\n");
    if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
        printf("can't reset SIGQUIT\n");
}

int main() {
    sigset_t newmask, oldmask, pendmask;

    if (signal(SIGQUIT, sig_quit) == SIG_ERR)
        printf("can't catch SIGQUIT\n");

    /* Block SIGQUIT and save current signal mask. */
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGQUIT);

    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
       printf("SIG_BLOCK error");
    sleep(5);   /* SIGQUIT here will remain pending */
    if (sigpending(&pendmask) < 0)
       printf("sigpending error");
    if (sigismember(&pendmask, SIGQUIT))
       printf("\nSIGQUIT pending\n");

    /* Restore signal mask which unblocks SIGQUIT. */
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
       printf("SIG_SETMASK error");
    printf("SIGQUIT unblocked\n");
    sleep(5);   /* SIGQUIT here will terminate with core file */

    return 0;
}

在您的上下文文件中包含以下代码:

/**
 * @BeforeScenario
 */
function initialiseStorage(BeforeScenarioScope $scope)
{
    $con = $this->getService('database_connection');

    $con->executeUpdate("SET foreign_key_checks = 0;");

    $filePath = $this->getMinkParameter('files_path').'/test_db.sql';
    $con->exec(file_get_contents($filePath));

    $con->executeUpdate("SET foreign_key_checks = 1;");
}

不要忘记在behat配置文件中添加以下关于上下文的行,以便能够在构造函数中使用实体管理器,然后在 initialiseStorage 方法中使用:

/**
 * @BeforeScenario
 */
function initialiseStorage(BeforeScenarioScope $scope)
{
    $con = $this->em->getConnection();

    $con->executeUpdate("SET foreign_key_checks = 0;");

    $filePath = $this->getMinkParameter('files_path').'/test_db.sql';
    $con->exec(file_get_contents($filePath));

    $con->executeUpdate("SET foreign_key_checks = 1;");
}

基本上为了获得连接,我们已经取代了:

private $em;

public function __construct(EntityManagerInterface $em)
{
    $this->em = $em;

}

由:

- AppBundle\Features\Context\FeatureContext:
    em: '@doctrine.orm.default_entity_manager'

答案 3 :(得分:0)

我建议你阅读有关工厂女孩模式的内容。这个想法是创造一个 您拥有的每个类的工厂,并在测试中使用它的实例。我使用https://github.com/carlescliment/handy-tests-bundle

其他选项将创建自己的步骤来创建实例或类似这样的类:

 /**
 * @Given /^there are products:$/
 */
public function thereAreRoutes(TableNode $table)
{
    $em = $this->getEntityManager();
    foreach ($table->getHash() as $hash) {
        $entity = new Product();
        $entity->setName($hash['name']);
        $entity->setDescription(isset($hash['description']) ? $hash['description'] : $hash['description']);
        $em->persist($entity);
    }

    $em->flush();
}

您可以像以下一样使用它:

Given there are products:
  | name  | description |
  | Shoes | It is blue  |