【问题标题】:Overide parent function in stub覆盖存根中的父函数
【发布时间】:2011-09-01 18:51:06
【问题描述】:

这对你们中的一些人来说可能很容易。我正在尝试在我拥有的小型数据库连接类上测试受保护的方法。

相关代码如下:

class DbConnect{

/**
 *    Connexion  MSSQL local
 */
protected function localConnect($localconfig){
    $connectionInfo = array("UID" => $localconfig->uid, 
                            "PWD" =>$localconfig->pwd, 
                            "Database"=> $localconfig->DB);

   $this->localConnection = sqlsrv_connect($localconfig->serverName,
                                           $connectionInfo);

   if( $this->localConnection === false ){
       $sql_error = sqlsrv_errors();
       throw new DBException("Error in DB Connection.\r\n
                              SQL ERROR:" . $sql_error);
   }
}
}

为了测试该方法,我有一个好主意(可能来自这里某处的帖子)子类化并从那里调用。我在测试文件的底部创建了一个子类。我显然无法覆盖该方法对公共的可见性,因此决定在存根中使用另一种方法:声明一个调用父级受保护的 localConnect 方法的公共方法:

 class DBConnectStub extends DBconnect{

   public function callLocalConnect($localConfig){
        parent::localConnect($localConfig);
    }
}

我的测试现在看起来像这样:

/**
 * @expectedException DBException
 */
public function test_localConnectError(){

  $localconfig = (object) array ( 'serverName' => 'nohost', 
                                   'uid' => 'nouid',
                                  'pwd' => 'noPwd',
                                  'DB' => 'noDB'

                         );  

  $db = DbConnectStub::getInstance($localconfig, array());
  $db->callLocalConnect($localConfig);
  unset($db);

}

奇怪的部分,当我运行测试时,php 吐了出来:

致命错误:调用 C:\tirelinkCRMsync\test 中未定义的方法 DbConnect::callLocalConnect() \tirelinkCRMSync\DBConnectTest.php 在第 82 行。

对象已正​​确实例化,但为什么没有定义方法,肯定有一个细节让我无法理解。这种方法有效还是有更好的方法?

【问题讨论】:

  • 这可能是由于 $localConfig 变量拼写错误。它应该是 $localconfig,不是吗?

标签: php phpunit


【解决方案1】:

这可能是个愚蠢的问题,但是您是否重写了 DbConnectStub::getInstance 以使其返回 Stub 实例?

class DBConnectStub extends DBconnect{
 public static function getInstance ()
 {
    //whatever process to create the instance (and not the parent method call that will return a DBConnect instance)
 } 

 public function callLocalConnect($localConfig){
    parent::localConnect($localConfig);
 }
}

【讨论】:

  • 嘿。一点都不傻!我即将结束 16 小时的编码狂欢,所以这个已经过去了。我的想法是使用 $c = __CLASS__;自我::$instance = new $c($params);会为 subs 动态设置类,但显然不是。感谢朋友提供线索。
【解决方案2】:

我正在尝试测试受保护的方法 [...]

不要

就这么简单。只是不要。受保护的方法不是类公共 API 的一部分,因此在尝试确保您的类正常工作时,您不应假设它们是如何工作的。

您应该能够在不调整测试的情况下更改您的代码(实现您的公共函数)。这就是您的测试制作的目的,以便您可以更改你的代码,你确定它仍然有效。当您同时更改代码和测试时,您无法确定您的代码仍然像以前一样工作!

见:Sebastian Bergmann -Testing Your Privates.html

所以:仅仅因为受保护和私有属性和方法的测试是可能的,并不意味着这是一件“好事”。

和:Best practices to test protected methods with PHPUnit - on abstract classes

这篇文章还提到的只是使用

$method = new ReflectionMethod(
    'Foo', 'doSomethingPrivate'
);
$method->setAccessible(TRUE);

这比为您要测试的每个方法创建一个子类更容易。


迂腐侧节点:

恕我直言,它应该是$this->localConnect 而不是parent::localConnect,因为 parent:: 仅用于调用父类的 same 方法。 (没关系,至少对我来说只是令人困惑)。

【讨论】:

  • 这里有很好的建议,我会注意这一点。
  • 感谢 edorian,此外,由于构造函数是私有的,因此我在这方面遇到了另一面墙,所以 php 出现以下问题:致命错误:从 C:\ 中的上下文 'DBConnectStub' 调用私有 DbConnect::__construct()轮胎 inkCRMsync\test\tirelinkCRMSync\DBConnectTest.php 在第 103 行
  • 关于私有/受保护方法的测试,这个单例同时建立了到2个不同数据库的2个数据库连接。客户端类不直接访问这些方法,但它们不应该由初始开发人员测试以确保它们在部署时按客户端类的预期工作吗?
  • 因为你不能调用你继承的对象的私有方法,是的。喜欢它应该可以工作;)->setAccessible 仍然可以工作,但 $everything_i_said_above :) ------- 第二条评论:a)不要使用单例(但我们/SO之前告诉过你)。 b) 该类有一个"getInstance" 方法,它应该返回一个DB 对象。该类没有做更多的事情,所以没有什么要测试的了。如果我理解正确的话。
  • 在重新阅读和重新思考之后,我现在理解得更好了。感谢您坚持这一点,因为它现在很有意义!如果我测试对象上的所有公开方法,我应该关心这些公共方法的结果!不是如何公共方法得到结果。如果公共方法失败,那么并且只有在那时我才应该查看黑盒代码。我想重新阅读关于测试私人如何影响代码覆盖率的部分,然后再打电话给这一天,但找不到关于那个的帖子。还回来给你发了一个major谢谢你让我更好地掌握了这个主题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-27
  • 1970-01-01
  • 2011-05-14
  • 2017-09-30
  • 2011-01-29
相关资源
最近更新 更多