测试是否调用了受保护的方法

Lui*_*mim 5 phpunit

我试图测试是否在公共接口中调用受保护的方法.

<?php
abstract class SomeClassAbstract
{ 
    abstract public foo();

    public function doStuff() 
    {    
        $this->_protectedMethod();
    }

    protected function _protectedMethod();
    {
        // implementation is irrelevant
    }
}

<?php
class MyTest extends PHPUnit_Framework_TestCase
{
    public function testCalled()
    {
        $mock = $this->getMockForAbstractClass('SomeClass');
        $mock->expects($this->once())
             ->method('_protectedMethod');

        $mock->doStuff();
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道它被正确调用,但PHPUnit说它从未调用过.

当我测试另一种方式时,从未调用方法时会发生同样的情况:

<?php
abstract class AnotherClassAbstract
{ 
    abstract public foo();

    public function doAnotherStuff() 
    {    
        $this->_loadCache();
    }

    protected function _loadCache();
    {
        // implementation is irrelevant
    }
}

<?php
class MyTest extends PHPUnit_Framework_TestCase
{
    public function testCalled()
    {
        $mock = $this->getMockForAbstractClass('AnotherClass');
        $mock->expects($this->once())
             ->method('_loadCache');

        $mock->doAnotherStuff();
    }
}
Run Code Online (Sandbox Code Playgroud)

调用该方法,但PHPUnit说它不是.

我做错了什么?

编辑 我没有用双冒号声明我的方法,它只是表示它是一个公共方法(接口).已更新为完整的类/方法声明.

编辑2 我应该说我在抽象类中测试一些方法实现(编辑代码来反映这一点).由于我无法实例化该类,我该如何测试呢?

我正在考虑创建一个SomeClassSimple扩展SomeClassAbstract并测试这个.这是正确的方法吗?

Ewa*_*odd 10

在我所拥有的PHPUnit版本中,该类PHPUnit_Framework_MockObject_Mock使用PHP get_class_methods来确定被模拟对象的接口. get_class_methods将只挑选公共方法,而不是受保护或私人方法.

这符合xUnit单元测试的精神.考虑PHPUnit文档关于如何使用Mock对象的示例.在那里,SUT是Subject一种受保护的方法notify.但是,正在测试的方法是doSomething.考虑Subject到黑盒子,我们不关心它是如何实现的细节.但是,我们关心它的行为.具体来说,我们要求在调用时调用任何附加观察者doSomethingupdate方法.所以我们模拟了第三方对象Observer,并设置了update调用其方法的期望.请注意,虽然执行了受保护的方法notify,但在测试中未明确命名.这使我们可以随时随地改变实现,只要行为得以保留.

回到你的例子.

class MyTest extends PHPUnit_Framework_TestCase
{
  public function testCalled()
  {
    $mock = $this->getMock('SomeClass');
    $mock->expects($this->once())
         ->method('_protectedMethod');

    $mock->doStuff();
  }
}
Run Code Online (Sandbox Code Playgroud)

在这里,您的testCalled方法不会创建被测系统(SUT)的实例,这将是SomeClass.doStuff在SomeClass 的模拟中的版本没有做任何事情.特别是,它没有打电话_protectedMethod.

  • 我认为,单元测试更像是一种艺术,而不是它看起来.阅读xUnit Test Patterns:Gerard Meszaros重构测试代码之后,我对此表示赞赏.反过来,这本书是PHPUnit作者Sebastian Bergmann的强烈推荐. (2认同)