3

我浏览了有关单元测试的各种问题,但找不到专门回答这个问题的问题。

我有几个 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,根据单元测试原则,我应该单独隔离每个函数。

我只是没有办法做到这一点。我在这里想念什么?

4

5 回答 5

2

这是一个通用的答案,因为我根本不熟悉 Propel,只是稍微熟悉 PHP。基本答案是您使用依赖注入。不是直接引用您的 ORM,而是围绕它创建一个包装器,然后将包装器注入到您的类/函数中以实际使用。要进行单元测试,您需要创建一个模拟或伪造的包装器版本,它不与 ORM 接口,而是让您配置从包装器到方法调用的响应。这允许您在对函数进行单元测试时排除 ORM。

于 2009-03-23T14:23:15.400 回答
1

我发现模拟 ORM 并没有给我任何信心,因为 ORM 配置从未经过测试。ORM 还具有许多远距离作用,这可能会给单元测试带来错误的信心。模拟数据库驱动程序或提供一个备用内存数据库让我更有信心我的代码是正确的,并且与模拟 ORM 一样难。

SQLite 是用于单元测试的出色内存数据库。它在 PDO 支持的数据库列表中。(PDO 是 Propel 1.3 数据库驱动程序。)如果您不想使用内存数据库,您可以找到已经编写好的 PDO 模拟。

于 2009-03-23T14:56:51.653 回答
1

在为 Symfony构建 PHPUnit 插件时,我试图解决同样的问题。我最终以类似于Django 的测试框架的方式接近它——使用单独的数据库/连接,并在每次测试之前销毁并重建它。

我发现在测试运行中的第一个测试之前(或者如果测试明确指示它),我也能够只重建测试数据库;在其他测试之前,它只是删除所有数据以加快速度。

于 2011-12-13T18:06:40.593 回答
0

我最近一直在阅读Misko Hevery 关于测试的博客。它涵盖了这种情况;您需要使用 DI(依赖注入)。

我也在为此苦苦挣扎,我也使用推进器。

一方面,您可以将“挂起”方法移动到“对象”类而不是对等方。无论如何,对于这个特定的功能,您不需要使用静态方法来实现这一点。您的 API 可能如下所示:

MyObjectPeer::retrieveByPK(1)->suspend();

这可以通过正常的单元测试方法进行测试。

如果它真的是需要测试的数据库,那么 AFAIK 你需要真正让数据库参与测试。我在我当前的项目中经常使用 ltree 和 postgis,除了将它包含在我的测试中之外,我想不出任何其他方法来为依赖于数据库的模型逻辑运行单元测试。

于 2009-03-23T21:06:12.653 回答
0

这是一个不能进行单元测试的具有硬依赖的类的例子。

我们可以通过与另一个数据库的连接进行测试,但是它不再是单元测试而是集成测试。

我想到的更好的选择是拥有一个 QueryFactory 类,它将包装您需要的所有不同方法,然后您将能够模拟它。

首先我创建一个界面

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
    }
}

有查询工厂注入的业务逻辑

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");
    }
}
于 2015-07-08T00:30:41.763 回答