覆盖存根中的父函数

发布于 2024-11-09 19:07:36 字数 1767 浏览 3 评论 0原文

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

相关代码如下:

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 行。

该对象已正确实例化,但为什么未定义该方法,肯定有一个细节让我困惑。这种方法有效还是有更好的方法?

This is probably an easy one for some of you. I'm trying to test a protected method on a small DB connection class I have.

Relevant code is as follows:

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

To test the method, I had the bright idea (probably from a post here somewhere) to subclass and call from there. I created a subclass, right at bottom of my test file. I obviously could not override the visibility of the method to public, so decided another approach in the stub: declare a public method that calls the parent's protected localConnect method:

 class DBConnectStub extends DBconnect{

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

My test now looks like this:

/**
 * @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);

}

The weird part, when I run the test, php spits out:

Fatal error: Call to undefined method DbConnect::callLocalConnect() in C:\tirelinkCRMsync\test
\tirelinkCRMSync\DBConnectTest.php on line 82.

The object is properly instanciated, but why is the method not defined, surely there is a detail that has eluded me. Is this approach valid or is there a better way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

你的心境我的脸 2024-11-16 19:07:36

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

不要

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

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

请参阅:Sebastian Bergmann - 测试您的隐私.html

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

以及: 最佳使用 PHPUnit 测试受保护方法的实践 - 在抽象类上

这篇文章还提到,使用

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

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


迂腐的侧节点:

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

I'm trying to test a protected method [...]

DON'T

It's as simple as that. Just don't. Protected methods are not part of the classes public API and therefore you should not make assumptions on how they work when trying to make sure your class works.

You should be able to change your code (implementation of your public functions) without adapting your tests. Thats what your tests are made for, so that you can change your code and you are sure that it still works. You can't be sure your code still works like before when you change your code and your tests at the same time!

See: Sebastian Bergmann -Testing Your Privates.html

So: Just because the testing of protected and private attributes and methods is possible does not mean that this is a "good thing".

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

What this post also mentions is to just use

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

Which is easier than to create a subclass for every method you want to test.


Pedantic side node:

Imho it should be $this->localConnect and not parent::localConnect because parent:: is only for calling the same method of the parent class. (Doesn't matter much, just confusing, for me at least).

沦落红尘 2024-11-16 19:07:36

这可能是一个愚蠢的问题,但是您是否重写了 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);
 }
}

This may be a stupid question, but did you override DbConnectStub::getInstance for it to return a Stub instance ?

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);
 }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文