你如何做单元测试与使用的ORM的应用程序?
-
21-08-2019 - |
题
我已经通过单元测试的各种问题看,但无法找到一个明确回答了这个问题。
我有一个包含函数看起来像这样几个PHP类:
static function _setSuspended($Suspended, $UserID)
{
try {
$con = Propel::getConnection();
$c1 = new Criteria();
$c1->add(DomainsPeer::USERID,$UserID);
$update = new Criteria();
$update->add(DomainsPeer::SUSPENDED,$Suspended);
BasePeer::doUpdate($c1, $update, $con);
return true;
} catch(PropelException $e) {
return $e->getMessage();
}
}
我使用的Propel我的ORM。我已经通过各种单元测试主题,谈创建“嘲弄”和“存根”,什么不读,但我一直没能找到任何具体告诉你如何测试功能像上面。
我的想法去是这样的:我需要测试上面,所以我想叫它功能。但是,如果我调用它,它使用行走作为ORM并根据该单元测试原则我应该本身隔离每个功能。
我只是不明白的方式来做到这一点。缺少什么我在这里?
解决方案
我发现,讽刺的ORM没有给我任何信心,因为ORM的配置从来没有得到检验。奥姆斯也有大量的行动在一定距离的效果,可以给单元测试虚假的信心。惩戒数据库驱动程序或提供一个备用内存数据库给我高得多的置信度我的代码是正确的并且是关于硬如讽刺ORM。
SQLite是一个伟大的内存数据库进行单元测试。这是PDO支持的数据库列表中。 (PDO是波轮1.3数据库驱动程序。)如果你不想使用一个内存数据库,你也许能找到已经写了PDO模拟。
其他提示
这是一个通用的答案,我不是在所有熟悉的Propel和唯一有点更熟悉PHP。基本的答案是,你使用依赖注入。而是直接指到你的ORM,你创建围绕它的包装,然后注入到包装类/功能实际使用。做单元测试,然后你创建一个不接口的ORM包装的模仿或假冒的版本,而是让你配置你的方法调用从包装的响应。这使您可以分解出ORM当单元测试的功能。
我试图同时建立了 PHPUnit的插件为symfony1.2 解决同样的问题。我结束了类似的接近它 Django的测试框架 - 使用单独的数据库/连接,并破坏和每次测试前重建它
我发现我也可以只在试运行第一个测试之前重建测试数据库脱身(或者,如果测试它明确指示);其他测试之前,它只是删除所有数据以加快速度一点。
我一直在阅读 MISKO Hevery的有关测试很多博客最近。它涵盖了这种情况;你需要使用DI(依赖注入)。
我与这个有点挣扎自己为好,我也用推进。
有关一个,可以移动“暂停”的方法“对象”级,而不是对等体。对于反正这特定的功能,你并不需要使用静态方法来实现这一目标。您的API可能看起来像:
MyObjectPeer::retrieveByPK(1)->suspend();
这将是经由正常的单元测试方法测试的。
如果它真的需要被测试的数据库,然后AFAIK你需要居然有DB参与测试。我使用的ltree和PostGIS的很多在我目前的项目,我想不出任何其他的方式来运行依赖于数据库以外,包括它在我的测试模型逻辑单元测试。
这是与硬依赖性不能为单元测试的类的为例。
我们可以能够与另一个数据库的连接测试,但随后,它不是一个单元测试了,但一个集成测试。
在更好的选择,我想是有一个QueryFactory类将包装所有你所需要的不同的方法,然后,你就可以嘲笑它。
首先,在第一I创建接口
interface iQueryFactory
{
function firstFunction($argument);
function secondFunction($argument, $argument2);
}
您所有的ORM请求QueryFactory我们需要
class QueryFactory implements iQueryFactory
{
function firstFunction($argument)
{
// ORM thing
}
function secondFunction($argument, $argument2)
{
// ORM stuff
}
}
有与该查询工厂的注射
的业务logiqueclass BusinessLogic
{
protected $queryFactory;
function __construct($queryFactoryInjection)
{
$this->queryFactory= $queryFactoryInjection;
}
function yourFunctionInYourBusinessLogique($argument, $argument2)
{
// business logique
try {
$this->queryFactory->secondFunction($argument, $argument2);
} catch (\Exception $e) {
// log
// return thing
}
// return stuff
}
}
在模拟部分,请注意,我不使用mock框架我为例(顺便说一句,你可以创建一个响应的setter)
class QueryFactoryMock implements iQueryFactory
{
function firstFunction($argument)
{
if (is_null($argument))
{
throw new \Exception("");
}
else
{
return "succes";
}
}
function firstFunction($argument, $argument2)
{
// sutff
}
}
后来终于单元测试谁测试我们的业务逻辑与模拟执行
class BusinessLogicTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
require_once "BusinessLogic.php";
}
public function testFirstFunction_WhenInsertGoodName()
{
$queryMockup = new QueryFactoryMock();
$businessLogicObject = new BusinessLogic($queryMockup);
$response = $businessLogicObject ->firstFunction("fabien");
$this->assertEquals($response, "succes");
}
public function testFirstFunction_WhenInsetNull()
{
$queryMockup = new QueryFactoryMock();
$businessLogicObject = new BusinessLogic($queryMockup);
$response = $businessLogicObject->firstFunction(null);
$this->assertEquals($response, "fail");
}
}