我在Symfony2 / Doctrine2应用程序上引入功能性behat测试,并决定如何处理数据库隔离和数据夹具。
使用自己完全独立的mysql数据库建立一个单独的测试环境是否存在任何陷阱,该数据库在执行behat测试套件之前由转储导入填充,然后在套件执行后清空?我正在努力避免使用数据夹具,除非我真的需要这样以节省手动编写外键关系的时间而不是。
感谢任何指导。
答案 0 :(得分:4)
就Symfony / Doctrine / Behat而言,如果你想成为那些遵循最佳实践的人之一:
答案 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 |