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

发布于 2024-10-17 07:59:09 字数 822 浏览 7 评论 0原文

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

“最佳实践使用 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:问题不在于测试受保护方法的真实性(参见:白盒测试、灰盒测试和黑盒测试)。测试受保护方法的需要取决于您的测试策略。

With PHPUnit and PHP >= 5.3 it is possible to test protected methods. The following page at stackoverflow outlined the best practice on it:

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

To test public methods on abstract classes is easy with PHPUnit.
To test protected methods on normal classes is easy with approach above.
To test protected methods on abstract classes must be possible somehow...

I know PHPUnit derives abstract classes and "implements" abstract methods in a concrete class and fires the tests against that concrete class - but i do not know how to integrate that into the approach above to have a callProtectedMethodOnAbstractClasses().

How are you doing such tests?

PS: The question is NOT about the truth of testing protected methods (see: white-, gray- and blackbox-testing). The need of testing protected methods depends on your test strategy.

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

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

发布评论

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

评论(2

浊酒尽余欢 2024-10-24 07:59:09

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

不要测试受保护和私有方法

仅仅因为您可以并不意味着您应该。

您想测试一个类是否有效。这意味着您可以调用的所有函数(所有公共函数)返回正确的值(并且可能对传入的对象调用正确的函数)什么也没有否则。

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

恕我直言,为任何非公开的内容编写测试甚至会伤害您,原因有两个:

  • 时间

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

  • 有意义的代码覆盖率

如果您为每个受保护的方法编写测试,您就会从代码覆盖率报告中受益:它不会告诉您哪些受保护的函数不再被调用。这是(恕我直言)一件坏事,因为您要么没有正确测试所有公共方法(如果您测试每种情况,为什么没有调用一个方法?)或者您真的不需要该方法不再但因为它是“绿色”的,所以你不会再考虑它。

引用 PHPUnit 作者的话

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

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

由于现实世界有时会有所不同

...->setAccessible() 对于

抽象内容使用的普通方法来说很好 ...->getMockForAbstractClass( )

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

抽象类中的受保护方法将通过测试其子级的公共 api 来进行测试,无论如何,我的论点都适用。

Since you are asking for a 'best practice' I'll take a different approach to answer:

Don't test protected and private methods

Just because you can doesn't mean you should.

You want to test that a class works. That means that all the functions you can call on it (everything public) return the right values (and maybe call the right functions on objects passed in) and nothing else.

You don't care how this is implemented in the class.

Imho it is even hurting you to write test for anything non-public for two big reasons:

  • Time

Writing tests takes longer as you need more and refactoring also takes longer. If you move around code in a class without changing its behavior you shoudn't be required to update its tests. The tests should tell you that everything still works!

  • Meaningful code coverage

If you write a test for every protected method you loose one inherit benefit from the code coverage report: It won't tell you which protected functions aren't call anymore. That is (imho) a bad thing because you either don't test all the public methods correctly (why is there a method that isn't called if you test every case?) or you really don't need that method anymore but since it's "green" you don't give it a second thought.

To quote the PHPUnit Author

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

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

Since the real world sometimes differs

...->setAccessible() is fine for normal methods

for abstract stuff use ...->getMockForAbstractClass()

But please do so only if it is really necessary.

A protected method in an abstract class will get tested by testing the public api of its children anyway with my arguments from above applying.

瞄了个咪的 2024-10-24 07:59:09

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

为抽象类创建一个模拟对象,并将其传递给 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() 来决定如何构造对象。

Assumption: You want to call concrete protected methods on an abstract class.

Create a mock object for the abstract class, and pass it to this modified form of 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());
}

You could encapsulate this into a single method, but I prefer to pass in the object so that a test can call multiple protected/private methods on the same object. To do so, use $class->isAbstract() to decide how to construct the object.

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