持续集成,将实际测试数据输入数据库的最佳实践,使用 Propel ORM
Continuous Integration, best practice to input actual test data into database, using Propel ORM
我使用 Propel ORM 复制了一个 table 模式,以便进行持续集成,但是 Propel 只给我一个完全充实的模式,它没有给我测试数据(或基本必要的数据)完全没有)。
如何从版本受控的 live/test 数据库中获取数据 propel-gen
Propel ORM 生态系统?
他们说 "best practice" 根本不存在 - 它是如此主观,以至于人们应该接受几种形式的 "good practice" 中的一种。我 认为 以下符合该标签的条件 - 最终它对我来说效果很好。我已经使用 PHPUnit 大约一年了,也许在我的项目中从头开始使用了六个月。
这是我在 PHPUnit bootstrap 阶段(在 phpunit.xml
中指定)所做的工作的概要:
- 删除并创建
myproject_test
数据库
- 在生成的 SQL
的预迁移副本上调用 insert-sql
Propel 命令
- 调用
migrate
推进命令
- 扫描我的测试文件夹以构建 classes 以设置测试,然后依次 运行 每个
手动插入 SQL 然后 运行 迁移的好处是迁移得到了真正彻底的测试。这特别方便,因为在开发中我有时会做一个 down
,修改一个迁移 class,然后做一个 up
来重新 运行 它:因此让人放心知道它将 运行 按顺序排列。目前我打算永久保留我所有的移民历史;虽然它会给测试和新构建增加非常小的延迟,但升级部署不会受到影响。
由于我的构建依赖于旧的 SQL 文件,因此我避免使用 sql
生成命令;如果意外发布,修改后的 SQL 文件可以在版本控制中轻松恢复。
目前,我只是在localhost
上使用myproject_test
的数据库名称,这样测试在运行的任何地方都不会影响其他数据库。在构建服务器上,您可能需要使用不同的凭据进行连接:考虑在 switch()
语句中检测机器名称,并相应地选择连接详细信息。
为了给你测试数据,我一般倾向于建议你不要使用从你的实时系统导出数据。一方面,它通常太多了,而且您通常希望为每个测试创建数据片段,以便测试完全隔离。我认为这是一个好主意,原因有二:
- 您可以并行化独立的测试。因此,当您的浏览器测试套件需要五个小时才能 运行 (!) 您可以设置更多构建服务器以更快地获得绿色构建。
- 您可能希望在本地 运行 测试套件本身,或测试本身,或匹配特定字符串的一组测试,如果一个测试依赖于另一个测试,这可能不起作用.
这是我的构建器 classes 发挥作用的地方。我在我的 bootstrap.php
中使用它,并在包含测试 classes:
的每个文件夹上调用它
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");
}
}
}
在!CommonBuild.php
中,我添加了不会被测试修改的只读数据,因此只有一个副本是安全的。
每个 PHPUnit 测试 class 我有一个构建 class:对于我拥有的每个 *Test.php
文件,我都会有一个对应的 *Build.php
。在每个构建器中,一个 build
静态方法被调用,并且我手动 运行 一个方法用于每个需要构建的东西的测试。这是一个简单的:
public static function build()
{
self::buildWriteVarToFieldSuccessfully();
self::buildWriteVarToFieldUsingFailedMatch();
self::buildWriteVarToFieldUsingFoundMatch();
self::buildFailIfVariableIsAnArray();
}
在未来的某个时候,我可能会使用反射来自动 运行 这些,就像 PHPUnit 用于测试一样,但现在没问题。
现在,在我的 bootstrap 脚本中,我使用测试连接完全初始化了 Propel,因此可以使用普通的 Propel 语句。因此,我将只创建我需要的数据,如下所示:
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');
}
我有一个命名约定,在测试 class 中对 testWriteVarToFieldUsingFoundMatch
的测试在相应的构建 class 中得到一个名为 buildWriteVarToFieldUsingFoundMatch
的构建器。它并没有在代码中强制执行,但这种命名有助于轻松找到一个给定的另一个(我经常使用 IDE 的分屏功能同时编辑两者)。
因此,在上面的示例中,我只需要一个雇主记录、一个工作记录、一个过程记录和一个源记录来 运行 这个特定的测试(而不是整个实时导出)。源记录被赋予一个与测试名称相关的唯一名称,因此它只会在本次测试中使用(我发现我必须注意这里的复制和粘贴错误 - 使用测试中的错误数据!)。
创建此类测试数据非常容易,无论您拥有何种数据库:user.name
字段、address.line1
字段等通常都可以创建包含唯一标识符,因此当您在测试中修改此数据,您知道只有该测试会使用它,因此它与其他测试隔离。
为了简单起见,我选择 运行 bootstrap 中的所有构建器,而不管正在进行什么测试 运行。因为这只需要额外的 15 秒,所以在我的例子中,可能不值得做更复杂的事情。但是,如果您愿意,可以在每个 PHPUnit 测试中使用 setUp
方法做一些巧妙的事情,检测当前测试(如果可能)然后 运行 适当的构建 class.
我使用 Propel ORM 复制了一个 table 模式,以便进行持续集成,但是 Propel 只给我一个完全充实的模式,它没有给我测试数据(或基本必要的数据)完全没有)。
如何从版本受控的 live/test 数据库中获取数据 propel-gen
Propel ORM 生态系统?
他们说 "best practice" 根本不存在 - 它是如此主观,以至于人们应该接受几种形式的 "good practice" 中的一种。我 认为 以下符合该标签的条件 - 最终它对我来说效果很好。我已经使用 PHPUnit 大约一年了,也许在我的项目中从头开始使用了六个月。
这是我在 PHPUnit bootstrap 阶段(在 phpunit.xml
中指定)所做的工作的概要:
- 删除并创建
myproject_test
数据库 - 在生成的 SQL 的预迁移副本上调用
- 调用
migrate
推进命令 - 扫描我的测试文件夹以构建 classes 以设置测试,然后依次 运行 每个
insert-sql
Propel 命令
手动插入 SQL 然后 运行 迁移的好处是迁移得到了真正彻底的测试。这特别方便,因为在开发中我有时会做一个 down
,修改一个迁移 class,然后做一个 up
来重新 运行 它:因此让人放心知道它将 运行 按顺序排列。目前我打算永久保留我所有的移民历史;虽然它会给测试和新构建增加非常小的延迟,但升级部署不会受到影响。
由于我的构建依赖于旧的 SQL 文件,因此我避免使用 sql
生成命令;如果意外发布,修改后的 SQL 文件可以在版本控制中轻松恢复。
目前,我只是在localhost
上使用myproject_test
的数据库名称,这样测试在运行的任何地方都不会影响其他数据库。在构建服务器上,您可能需要使用不同的凭据进行连接:考虑在 switch()
语句中检测机器名称,并相应地选择连接详细信息。
为了给你测试数据,我一般倾向于建议你不要使用从你的实时系统导出数据。一方面,它通常太多了,而且您通常希望为每个测试创建数据片段,以便测试完全隔离。我认为这是一个好主意,原因有二:
- 您可以并行化独立的测试。因此,当您的浏览器测试套件需要五个小时才能 运行 (!) 您可以设置更多构建服务器以更快地获得绿色构建。
- 您可能希望在本地 运行 测试套件本身,或测试本身,或匹配特定字符串的一组测试,如果一个测试依赖于另一个测试,这可能不起作用.
这是我的构建器 classes 发挥作用的地方。我在我的 bootstrap.php
中使用它,并在包含测试 classes:
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");
}
}
}
在!CommonBuild.php
中,我添加了不会被测试修改的只读数据,因此只有一个副本是安全的。
每个 PHPUnit 测试 class 我有一个构建 class:对于我拥有的每个 *Test.php
文件,我都会有一个对应的 *Build.php
。在每个构建器中,一个 build
静态方法被调用,并且我手动 运行 一个方法用于每个需要构建的东西的测试。这是一个简单的:
public static function build()
{
self::buildWriteVarToFieldSuccessfully();
self::buildWriteVarToFieldUsingFailedMatch();
self::buildWriteVarToFieldUsingFoundMatch();
self::buildFailIfVariableIsAnArray();
}
在未来的某个时候,我可能会使用反射来自动 运行 这些,就像 PHPUnit 用于测试一样,但现在没问题。
现在,在我的 bootstrap 脚本中,我使用测试连接完全初始化了 Propel,因此可以使用普通的 Propel 语句。因此,我将只创建我需要的数据,如下所示:
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');
}
我有一个命名约定,在测试 class 中对 testWriteVarToFieldUsingFoundMatch
的测试在相应的构建 class 中得到一个名为 buildWriteVarToFieldUsingFoundMatch
的构建器。它并没有在代码中强制执行,但这种命名有助于轻松找到一个给定的另一个(我经常使用 IDE 的分屏功能同时编辑两者)。
因此,在上面的示例中,我只需要一个雇主记录、一个工作记录、一个过程记录和一个源记录来 运行 这个特定的测试(而不是整个实时导出)。源记录被赋予一个与测试名称相关的唯一名称,因此它只会在本次测试中使用(我发现我必须注意这里的复制和粘贴错误 - 使用测试中的错误数据!)。
创建此类测试数据非常容易,无论您拥有何种数据库:user.name
字段、address.line1
字段等通常都可以创建包含唯一标识符,因此当您在测试中修改此数据,您知道只有该测试会使用它,因此它与其他测试隔离。
为了简单起见,我选择 运行 bootstrap 中的所有构建器,而不管正在进行什么测试 运行。因为这只需要额外的 15 秒,所以在我的例子中,可能不值得做更复杂的事情。但是,如果您愿意,可以在每个 PHPUnit 测试中使用 setUp
方法做一些巧妙的事情,检测当前测试(如果可能)然后 运行 适当的构建 class.