Phpunit 持续集成,使用Propel ORM将实际测试数据输入数据库的最佳实践

Phpunit 持续集成,使用Propel ORM将实际测试数据输入数据库的最佳实践,phpunit,continuous-integration,database-schema,propel,Phpunit,Continuous Integration,Database Schema,Propel,我使用Propel ORM复制一个表模式,以便进行持续集成,但Propel只为我提供了一个完全充实的模式,它没有为我提供测试数据(或基本的必要数据) 如何从带有版本控制的propel gen(propel ORM生态系统)的实时/测试数据库中获取数据?他们说,任何东西都不存在“最佳实践”——这是一种主观的做法,人们应该满足于几种形式的“良好实践”中的一种。我认为下面的内容符合这个标签的要求,而且最终对我来说效果很好。我已经使用PHPUnit大约一年了,也许六个月都是从头开始的 下面是我在PHPU

我使用Propel ORM复制一个表模式,以便进行持续集成,但Propel只为我提供了一个完全充实的模式,它没有为我提供测试数据(或基本的必要数据)

如何从带有版本控制的propel gen(propel ORM生态系统)的实时/测试数据库中获取数据?

他们说,任何东西都不存在“最佳实践”——这是一种主观的做法,人们应该满足于几种形式的“良好实践”中的一种。我认为下面的内容符合这个标签的要求,而且最终对我来说效果很好。我已经使用PHPUnit大约一年了,也许六个月都是从头开始的

下面是我在PHPUnit引导阶段所做工作的概要(在
PHPUnit.xml
中指定):

  • 删除并创建
    myproject\u测试
    数据库
  • 对生成的sql的预迁移副本调用
    insert sql
    spreep命令
  • 调用
    migrate
    spreep命令
  • 扫描我的测试文件夹中的生成类以设置测试,然后依次运行每个测试
手动插入SQL然后运行迁移的好处是,迁移得到了真正彻底的测试。这尤其方便,因为在开发过程中,我有时会执行
向下
,修改迁移类,然后执行
向上
重新运行它:因此,知道它将按顺序运行是令人放心的。目前,我计划永久保留我所有的移民历史;虽然这会给测试和新构建增加很小的延迟,但升级部署不会受到影响

因为我的构建依赖于一个旧的SQL文件,所以我避免使用
SQL
generation命令;如果它是意外发出的,那么修改后的SQL文件可以在版本控制中轻松地恢复

目前,我只是在
localhost
上使用一个数据库名
myproject\u test
,这样无论在哪里运行测试,其他数据库都不会受到影响。在构建服务器上,您可能需要使用不同的凭据进行连接:考虑在<代码>开关()/<代码>语句中检测机器名称,并相应地选择连接细节。 为了给您提供要测试的数据,我通常倾向于建议您不要使用从实时系统导出的数据。首先,通常有太多的数据,而且您通常希望在每个测试中创建数据片段,以便测试完全隔离。我认为这是一个好主意,有两个原因:

  • 您可以并行化独立的测试。因此,当您的浏览器测试套件运行五个小时(!)时,您可以设置更多构建服务器,以更快地获得绿色构建
  • 您可能希望自己在本地运行一个测试套件,或者自己运行一个测试,或者运行一组匹配某个字符串的测试,如果一个测试依赖于另一个测试,那么这可能不起作用
这就是我的生成器类的用武之地。我在我的
bootstrap.php
中使用它,并在包含测试类的每个文件夹中调用它:

function runBuilders($buildFolder, $namespace)
{
    // I use ! to mark common builders that need to be run first.
    // Since this confuses autoloader, I load that manually.
    $commonBuilder = $buildFolder . '/!CommonBuild.php';
    if (file_exists($commonBuilder))
    {
        require_once $commonBuilder;
    }

    foreach(glob($buildFolder . '/*Build.php') as $class)
    {
        $matches = array();
        $found = preg_match('#/([!a-zA-Z]+)\.php#', $class, $matches);
        if ($found)
        {
            echo '.';

            // Don't use ! characters when creating the class
            $className = str_replace('!', '', $matches[1]);
            call_user_func($namespace . "\\{$className}::build");
        }
    }
}
中!php
我添加了不会被测试修改的只读数据,因此只有一个副本是安全的

每个PHPUnit测试类有一个构建类:对于我拥有的每个
*test.php
文件,我将有一个相应的
*build.php
。在每个构建器中,都会调用一个
build
静态方法,这样我就可以为每个需要构建的测试手动运行一个方法。这里有一个简单的例子:

public static function build()
{
    self::buildWriteVarToFieldSuccessfully();
    self::buildWriteVarToFieldUsingFailedMatch();
    self::buildWriteVarToFieldUsingFoundMatch();
    self::buildFailIfVariableIsAnArray();
}
在将来的某个时候,我可能会使用反射来自动运行这些程序,就像PHPUnit对测试所做的那样,但现在还可以

现在,在我的引导脚本中,我使用测试连接完全初始化了推进,所以普通的推进语句可用。因此,我将创建所需的数据,如下所示:

protected static function buildWriteVarToFieldUsingFoundMatch()
{
    // Save an item in the holding table
    $employer = self::createEmployer();
    $job = new \Job\Model\JobHolding();
    $job->setReference('12345');
    $job->setLocationAlias('Rhubarb patch');
    $job->setEmployerId($employer->getPrimaryKey());
    $job->save();

    $process = self::createProcessingUsingRowMatching($employer);
    $process->createSource('VarToFieldTest_buildWriteVarToFieldUsingFoundMatch');
}
我有一个命名约定,即测试类中的
testWriteVarToFieldUsingFoundMatch
测试在相应的构建类中获得一个名为
buildWriteVarToFieldUsingFoundMatch
的构建器。在代码中没有这样的强制,但这种命名有助于轻松找到给定的一个(我经常使用IDE的分屏功能同时编辑这两个)

因此,在上面的示例中,我只需要一个雇主记录、一个工作记录、一个流程记录和一个源记录来运行这个特定的测试(而不是整个实时导出)。源记录被赋予了一个与测试名称相关的唯一名称,因此它只会在这个测试中使用(我发现我必须注意这里的复制和粘贴错误-在测试中使用错误数据非常容易!)

创建这种类型的测试数据非常容易,无论您拥有何种类型的数据库:
user.name
字段、
address.line1
字段等等,通常都可以创建包含唯一标识符的字段,以便在测试中修改此数据时,您知道只有该测试才会使用它,因此,它与其他测试隔离开来


出于简单的原因,我选择在引导中运行所有构建器,而不管正在运行什么测试。因为这只需要额外15秒,在我的情况下,可能不值得做更复杂的事情。但是,如果您愿意,您可以在每个PHPUnit测试中使用
设置
方法做一些聪明的事情,检测当前的测试(如果可能),然后运行适当的构建类。

@halfer是的,我很乐意听到这方面的进展,因为我很高兴将PHPUnit与codeship.io.Hmmm一起使用,我会检查一下,看看这个过程是否符合我的需要,谢谢。不幸的是,另外几天前我对propel的开发状态很好奇,发现库上的活动似乎已经逐渐减少,它似乎正在逐渐变成一个未维护的软件库,这意味着我实际上可能不得不完全