关于phpunit:测试抽象类

Testing Abstract Classes

如何用phpunit测试抽象类的具体方法?

我希望作为测试的一部分,我必须创建某种对象。不过,我不知道这方面的最佳实践,也不知道phpunit是否允许这样做。


抽象类的单元测试并不意味着测试接口,因为抽象类可以有具体的方法,而这种具体的方法可以被测试。

在编写一些库代码时,拥有您希望在应用程序层中扩展的某些基类并不少见。如果你想确保库代码是经过测试的,你需要一些方法来测试抽象类的具体方法。

就个人而言,我使用phpunit,它有所谓的存根和模拟对象来帮助您测试这类东西。

直接从phpunit手册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
abstract class AbstractClass
{
    public function concreteMethod()
    {
        return $this->abstractMethod();
    }

    public abstract function abstractMethod();
}

class AbstractClassTest extends PHPUnit_Framework_TestCase
{
    public function testConcreteMethod()
    {
        $stub = $this->getMockForAbstractClass('AbstractClass');
        $stub->expects($this->any())
             ->method('abstractMethod')
             ->will($this->returnValue(TRUE));

        $this->assertTrue($stub->concreteMethod());
    }
}

模拟对象为您提供以下几种功能:

  • 您不需要有抽象类的具体实现,可以用存根来代替。
  • 您可以调用具体的方法并断言它们的性能是正确的
  • 如果具体方法依赖于未实现(抽象)方法,则可以使用will()phpunit方法来存根返回值。


这是个好问题。我也一直在找这个。幸运的是,phpunit已经为这个案例提供了getMockForAbstractClass()方法,例如

1
2
3
4
5
protected function setUp()
{
    $stub = $this->getMockForAbstractClass('Some_Abstract_Class');
    $this->_object = $stub;
}

重要:

注意,这需要phpunit>3.5.4。在以前的版本中有一个错误。

要升级到最新版本:

1
2
sudo pear channel-update pear.phpunit.de
sudo pear upgrade phpunit/PHPUnit


应该注意的是,截至php 7,已经添加了对匿名类的支持。这为您提供了一种为抽象类设置测试的额外方法,一种不依赖于phpunit特定功能的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class AbstractClassTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @var AbstractClass
     */
    private $testedClass;

    public function setUp()
    {
        $this->testedClass = new class extends AbstractClass {

            protected function abstractMethod()
            {
                // Put a barebones implementation here
            }
        };
    }

    // Put your tests here
}


纳尔逊的回答是错误的。

抽象类并不要求它们的所有方法都是抽象的。

实现的方法是我们需要测试的方法。

您可以做的是在单元测试文件上创建一个假存根类,让它扩展抽象类,并且只实现完全没有功能的所需内容,当然,还要测试它。

干杯。


Eran,您的方法应该有效,但是它违背了在实际代码之前编写测试的趋势。

我建议您编写有关抽象类的非抽象子类所需功能的测试,然后编写抽象类和实现子类,最后运行测试。

显然,您的测试应该测试抽象类的定义方法,但总是通过子类。


如果您不想将抽象类子类化,只是为了对已经在抽象类中实现的方法执行单元测试,那么您可以尝试查看您的框架是否允许您模拟抽象类。