使用PHPUnit测试受保护方法的最佳实践(在抽象类上)

时间:2011-02-15 22:31:34

标签: php methods phpunit abstract protected

使用PHPUnit和PHP> = 5.3,可以测试受保护的方法。 stackoverflow的以下页面概述了最佳实践:

"Best practices to test protected methods with PHPUnit"

protected static function callProtectedMethod($name, $classname, $params) {
  $class = new ReflectionClass($classname);
  $method = $class->getMethod($name);
  $method->setAccessible(true);
  $obj = new $classname($params);
  return $method->invokeArgs($obj, $params);
}

使用PHPUnit轻松测试抽象类的公共方法。 使用上述方法可以轻松地测试正常类上的受保护方法。 要在抽象类上测试受保护的方法,必须以某种方式...

我知道PHPUnit派生抽象类并在具体类中“实现”抽象方法并针对该具体类触发测试 - 但我不知道如何将其集成到上面的方法中以获得callProtectedMethodOnAbstractClasses()。

你是如何进行此类测试的?

PS:问题不在于测试受保护方法的真相(参见:白色,灰色和黑盒测试)。测试受保护方法的需要取决于您的测试策略。

2 个答案:

答案 0 :(得分:24)

由于您要求“最佳实践”,我将采取不同的方法来回答:

不测试受保护和私有方法

只是因为你的意思并不代表你应该这样做。

您想测试一个类是否有效。这意味着你可以调用的所有函数它(一切都是公开的)返回正确的值(并且可能在传入的对象上调用正确的函数)而且没有其他即可。

你不关心这是如何在课堂上实现的。

Imho甚至会因为两个重要原因而为非公开的人编写测试而伤害你:

  • 时间

编写测试需要更长时间,因为您需要更多,重构也需要更长时间。如果您在类中移动代码而不更改其行为,则不需要更新其测试。测试应告诉你一切仍然有效!

  • 有意义的代码覆盖率

如果您为每个受保护的方法编写测试,则会从代码覆盖率报告中遗漏一个继承的好处:它不会告诉您哪些受保护的功能不再被调用。那是(imho)一件坏事,因为你要么没有正确地测试所有公共方法(为什么有一个方法在你测试每个案例时都没有被调用?)或者你真的不需要那个方法再好了但是因为它是“绿色”你不再考虑它。

引用PHPUnit作者

  

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

http://sebastian-bergmann.de/archives/881-Testing-Your-Privates.html

因为现实世界有时会有所不同

...->setAccessible()适用于普通方法

抽象内容使用...->getMockForAbstractClass()

但请仅在确实必要时才这样做。

抽象类中的受保护方法将通过测试其子项的公共api进行测试,无论如何都要使用上面的应用参数。

答案 1 :(得分:5)

假设:您想在抽象类上调用具体的受保护方法。

为抽象类创建一个模拟对象,并将其传递给此callProtectedMethod()的修改形式。

public static function callProtectedMethod($object, $method, array $args=array()) {
    $class = new ReflectionClass(get_class($object));
    $method = $class->getMethod($method);
    $method->setAccessible(true);
    return $method->invokeArgs($object, $args);
}

public function testGetArea() {
    $rect = $this->getMockForAbstractClass('RandomRectangle');
    self::callProtectedMethod($rect, 'setWidth', array(7));
    self::callProtectedMethod($rect, 'setHeight', array(3));
    self::assertEquals(21, $rect->getArea());
}

您可以将其封装到单个方法中,但我更喜欢传入对象,以便测试可以在同一个对象上调用多个受保护/私有方法。为此,请使用$class->isAbstract()来决定如何构造对象。