覆盖存根中的父函数

时间:2011-05-25 13:29:36

标签: php phpunit

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

相关代码如下:

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

为了测试这个方法,我有了一个好主意(可能是从这里的某个帖子)到子类并从那里调用。我在测试文件的底部创建了一个子类。我显然无法覆盖方法对public的可见性,因此决定了stub中的另一种方法:声明一个调用父的protected 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() 第82行上的\ tirelinkCRMSync \ DBConnectTest.php。

对象是正确实现的,但为什么方法没有定义,肯定有一个细节让我望而却步。这种方法有效还是有更好的方法?

2 个答案:

答案 0 :(得分:3)

  

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

就这么简单。只是不要。受保护的方法不是类公共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);

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


迂腐节点:

Imho它应该是$this->localConnect而不是parent::localConnect,因为parent ::仅用于调用父类的相同的方法。 (无所谓,至少对我来说很困惑)。

答案 1 :(得分:0)

这可能是一个愚蠢的问题,但你是否覆盖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);
 }
}