我已经查看了有关单元测试的各种问题,但找不到专门回答这个问题的问题。
我有几个包含如下函数的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。我已经阅读了各种单元测试主题,讨论创建“模拟”和“存根”以及什么不是,但我找不到任何具体告诉您如何测试上述功能的内容。
我的想法是这样的:我需要测试上面的函数,所以我想调用它。但是,如果我称之为它,它使用Propel作为ORM,并且根据单元测试原则,我应该单独隔离每个函数。
我只是没有办法做到这一点。我在这里缺少什么?
答案 0 :(得分:2)
这是一个通用的答案,因为我根本不熟悉Propel,只是对PHP更熟悉。基本答案是您使用依赖注入。不是直接引用你的ORM,而是在它周围创建一个包装器,然后将包装器注入你的类/函数中以实际使用。要进行单元测试,然后创建一个模拟或虚假版本的包装器,它不与ORM连接,而是允许您配置从包装器到方法调用的响应。这允许您在单元测试函数时分解ORM。
答案 1 :(得分:1)
我发现嘲笑ORM并没有给我任何信心,因为ORM配置永远不会被测试。 ORM在距离效应方面也有很多动作,这可能会对单元测试产生错误的信心。模拟数据库驱动程序或提供备用内存数据库让我更有信心,我的代码是正确的,并且与模拟ORM一样困难。
SQLite是一个用于单元测试的优秀内存数据库。它位于PDO支持的数据库列表中。 (PDO是Propel 1.3数据库驱动程序。)如果您不想使用内存数据库,您可能能够找到已编写的PDO模拟。
答案 2 :(得分:1)
我在构建PHPUnit plugin for Symfony时试图解决同样的问题。我最终接近它与Django's test framework类似 - 使用单独的数据库/连接,并在每次测试之前销毁并重建它。
我发现在测试运行中的第一次测试之前(或者如果测试明确指示它),我也能够只重建测试数据库;在其他测试之前,它只是删除所有数据以加快速度。
答案 3 :(得分:0)
我最近一直在阅读Misko Hevery's blog about testing。它涵盖了这种情况;你需要使用DI(依赖注入)。
我也在努力解决这个问题,我也使用推进器。
首先,您可以将“suspend”方法移动到“Object”类而不是对等方。无论如何,对于这个特定的函数,您不需要使用静态方法来实现此目的。您的API可能如下所示:
MyObjectPeer::retrieveByPK(1)->suspend();
这可以通过正常的单元测试方法测试。
如果它确实是需要测试的数据库,那么AFAIK你需要实际让数据库参与测试。我在当前项目中使用了ltree和postgis,我想不出任何其他方法来运行依赖于DB的模型逻辑的单元测试,而不是将其包含在我的测试中。
答案 4 :(得分:0)
这是一个具有硬依赖性的类的例子,它不能单元测试。
我们可以测试与另一个数据库的连接但是,它不再是单元测试,而是集成测试。
我想到的更好的选择是拥有一个QueryFactory类,它将包装你需要的所有不同的方法,然后,你将能够模拟它。
首先我创建一个接口
interface iQueryFactory
{
function firstFunction($argument);
function secondFunction($argument, $argument2);
}
QueryFactory包含我们需要的所有ORM请求
class QueryFactory implements iQueryFactory
{
function firstFunction($argument)
{
// ORM thing
}
function secondFunction($argument, $argument2)
{
// ORM stuff
}
}
注入查询工厂有业务逻辑
class 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
}
}
模拟部分,请注意我没有使用模拟框架作为我的例子(顺便说一句,你可以创建一个响应设置器)
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");
}
}