代码之家  ›  专栏  ›  技术社区  ›  GeoffreyF67

如何使用使用ORM的应用程序进行单元测试?

  •  2
  • GeoffreyF67  · 技术社区  · 17 年前

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

    我只是看不出有什么办法。我错过了什么?

    5 回复  |  直到 14 年前
        1
  •  2
  •   tvanfosson    17 年前

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

        2
  •  1
  •   Ken Fox    17 年前

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

        3
  •  1
  •   user212218 user212218    13 年前

    我在建立一个新的系统时,试图解决同样的问题 PHPUnit plugin for Symfony Django's test framework 使用单独的数据库/连接,并在每次测试之前销毁和重建它。

        4
  •  0
  •   apinstein    17 年前

    我一直在看书 Misko Hevery's blog about testing 最近很多。它涵盖了这种情况;您需要使用DI(依赖项注入)。

    我也在努力解决这个问题,我也使用了推进。

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

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

        5
  •  0
  •   user3045808    11 年前

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

    我考虑的更好的替代方法是使用QueryFactory类,它将封装您需要的所有不同方法,然后您将能够模拟它。

    首先,我首先创建一个接口

    interface iQueryFactory
    {
        function firstFunction($argument);
        function secondFunction($argument, $argument2);
    }
    

    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");
        }
    }