【问题标题】:PHPUnit mock/stub functionality within class类中的 PHPUnit 模拟/存根功能
【发布时间】:2011-12-26 00:17:38
【问题描述】:

我需要一些关于如何处理此问题的建议。

使用 PHP 一个例子是:

class BuilderClass {
 function getClass($id, $some, $vars){
  $dbResult = new db_Class::getDbRows($id, $some, $vars);
  foreach(...)
   // Build something from the database values

  return self;
 }
}

所以我想做的是创建一个测试用例,以某种方式模拟数据库结果。

我还没有找到任何很好的方法来做到这一点,请指出正确的方向或类似的方法来让这对我有用。

我可以在构建器本身中更改某些内容,例如调用运行该函数的类:FunctionRunner::runStaticFunction("db_Class", "getDbRows", $args, $something_else);但目前我不知道这是否可能。任何涵盖此内容的研究文章或任何解释此内容的网站。我现在什么都愿意。

谢谢 /马库斯

【问题讨论】:

    标签: php unit-testing mocking phpunit stub


    【解决方案1】:

    拆分从数据库中检索数据和构建数据的操作。

    class BuilderClass {
        function getClass($id, $some, $vars){
            $dbResult = new db_Class::getDbRows($id, $some, $vars);
            return doGetClass($dbResult);
        }
    
        function doGetClass($dbResult) {
            foreach(...)
             // Build something from the database values
    
            return self;   
        }
    }
    

    这样,您可以在调用数据库的情况下单独测试doGetClass

    【讨论】:

    • 真是个好主意,这样我就可以模拟 getClass 函数了。去做我想做的事。
    • 这个替代方案让我可以选择使用静态函数 getClass。我没有提到我现在有。
    • 我发现这本书在处理informit.com/store/product.aspx?isbn=0131177052 这样的情况时非常宝贵。我相信这种策略被称为模板重定义。
    • 那么,在您的示例中是否可以为“doGetClass”提供私有静态函数?我的意思是,phpunut 是否能够模拟私有静态函数?
    • @Oldek 使用这种策略你不需要模拟 getClass()。您可以只实例化一个 BuilderClass 的对象并通过传入一个模拟 db_Class 来针对 doGetClass 运行测试。
    【解决方案2】:

    通常情况下,无法轻松地为您的函数编写测试是由您的应用程序设计中的缺陷引起的。在这种情况下,db_Class 与您的 BuilderClass 紧密耦合。

    一个合适的解决方案是使用依赖注入在 BuilderClass 中创建一个 Database 对象,然后模拟该注入以返回静态结果。

    class BuilderClass
    {
        protected $oDatabase;
        public function __construct(db_Class $oDatabase) {
            $this->oDatabase = $oDataabse;
        }
    
        public function getClass($someVars) {
            $this->oDatabase->getDbRows($someVars);
        }
    }
    

    这样,数据库对象很容易被存根替换。

    【讨论】:

    • 是的,在理想情况下,我会朝着这种解决方案前进。这有点鸡和蛋,因为 OP 有他需要测试的工作代码,理想情况下他会在重构到这个解决方案之前对其进行测试。完全重建也是不可能的(或不可取的)。
    • 我很欣赏这一点,确实,我应该能够将其设为对象而不是静态函数,但是仅使用单独的函数来获取数据库行的想法。
    【解决方案3】:

    有很多方法可以做到这一点,但由于我们讨论的是 PHP,您可以利用 magic class loader function

    简单地说,如果你想模拟数据访问层,你只需使用数据类的实际名称创建一个对象,并且永远不会调用自动加载器。

    想要实际访问数据库?不定义类,当有东西试图访问数据库时会调用自动加载器,然后它应该知道如何加载类。

    大多数情况下,我的自动加载器在我使用时往往看起来像这样;

    function __autoload($className)
    {
      if(file_exists('../includes/'.$className.'.php'))
        require_once('../includes/'.$className.'.php');
    }
    

    【讨论】:

    • 那么您建议为 PHPUnit 单独自动加载?
    • 这是一种选择,或者可以直接定义一个模拟类。使用 IoC 模式是一个更好的解决方案,但由于这是 PHP,这是一个可能的选择 - 如果正在为其编写测试的系统是一个旧的、复杂的系统 - 它可能是一个更简单的解决方案。
    猜你喜欢
    • 2018-02-08
    • 2012-01-09
    • 2015-03-23
    • 1970-01-01
    • 2019-12-20
    • 1970-01-01
    • 2012-05-23
    • 2014-08-08
    • 2013-04-18
    相关资源
    最近更新 更多