【问题标题】:Code coverage when not testing protected/private methods with PHPUnit不使用 PHPUnit 测试受保护/私有方法时的代码覆盖率
【发布时间】:2015-10-13 17:36:52
【问题描述】:

我知道可以使用 PHPUnit 使用反射或其他变通方法来测试私有/受保护的方法。

但大多数消息来源告诉我,为类内部的私有方法编写测试不是最佳实践。

您应该像测试“黑匣子”一样测试该类——您只需通过比较输入与输出来测试预期行为,而忽略内部机制。为类编写测试还应该通过显示代码覆盖率不足来通知您未使用的私有方法。

当我测试我的类并生成 HTML 报告时,它显示私有方法未被测试覆盖,即使调用它们的行绝对被执行/覆盖。我知道私有方法已被执行,因为如果它们不是我的类的断言将不会通过。

这是 PHPUnit 中的预期行为吗?我能否争取 100% 的覆盖率,同时仍仅间接测试私有方法?

一些简化的示例代码(在 Symfony2 中使用 RestBundle):

class ApiController extends FOSRestController {

/*
 * @REST\View()
 * @REST\Get("/api/{codes}")
 */
public function getCodesAction($codes) {
    $view = new View();
    $view->setHeader('Access-Control-Allow-Origin', '*');
    $view->setData(array('type' => 'codes','data' => $this->_stringToArray($codes)));
    $view->setFormat('json')->setHeader('Content-Type', 'application/json');
    return $this->handleView($view);
}

private function _stringToArray($string){
    return explode('+',$string);
}

公共函数显示为“被覆盖”,私有函数被间接覆盖但在 PHPUnit 报告中显示为红色。

测试:

class ApiControllerTest extends WebTestCase {

    public function test_getCodesAction(){
        $client = static::createClient();
        $client->request('GET', '/api/1+2+3');
        $this->assertContains('{"type": "codes", "data": [1,2,3]}', $client->getResponse()->getContent());
    }

}

当然,这只是一个愚蠢的例子,我也可以在公共函数中包含explode();但我正在为其编写测试的控制器包含更复杂且可重用的私有函数,这些函数以更复杂的方式转换数据(但仍然没有副作用)。

【问题讨论】:

  • 测试私有方法本身不是问题——做这样的测试并不是一个坏习惯。它首先具有被认为是有害的私有方法,因为它们很难测试。
  • @narf,我不同意这一点。测试私有方法会使测试变得脆弱,如果您认为唯一的选择是通过反射进行测试,我会认为这是一种不好的代码气味。私有方法无害,它们是在类中组织代码的好方法。
  • @SamHolder Brittle 总比没有好...... 拥有测试是一种不好的做法没有逻辑,你无法说服我。 :) 无论如何,测试什么、如何测试以及测试到什么程度在很大程度上是一个基于意见的话题,所以我宁愿同意不同意你的观点。 :)
  • @narf 您应该根据可公开验证的行为进行测试。私有方法是一个实现细节。
  • @narf 很遗憾你对这个问题持封闭态度。您似乎将我的立场视为“不要在私有方法中测试代码”,而实际上它是“通过公共方法测试所有代码”。如果您碰巧将其中一些代码放在由公共方法调用的私有方法中,那很好。无关紧要,但很好'。请看我的回答herehere

标签: php unit-testing phpunit code-coverage


【解决方案1】:

在 Phpunit 中,您可以指定带有特殊注释的覆盖方法,如 doc 中所述。

你可以这样做:

    class ApiControllerTest extends WebTestCase {

        /**
         * @covers ApiController::getCodesAction
         * @covers ApiController::_stringToArray
         */
        public function test_getCodesAction(){
            $client = static::createClient();
            $client->request('GET', '/api/1+2+3');
            $this->assertContains('{"type": "codes", "data": [1,2,3]}', $client->getResponse()->getContent());
        }

    }

希望有帮助

【讨论】:

  • 嗨@SamHolder 当然,问题是关于代码覆盖率,所以我回答如何忽略/欺骗代码覆盖率。是的,细节中的方法可以包装在一个简单的 util 类中,该类应该在一个独立的范围内进行单元测试。
  • 嗨@Matteo。我不是在评论您的答案的质量,这很可能是完美的,更多的是如果这 is 是 G*d 帮助所有 PHP 开发人员的解决方案。
  • @SamHolder,对不起,我是单元测试的新手。我欢迎任何建议,但 PHPUnit 文档专门使用覆盖注释来进行覆盖率报告。或者您认为有更好的方法吗?另外,是的,我很乐意放弃 PHP/Symfony,转而使用 Haskell/Snap 之类的功能,但在现实世界中,你在拥有大量廉价初级开发人员的大型团队中工作,并且 90% 的 Web 开发都是被滥用的脏编码没有任何形式的规划或文档的 PHP 框架。而且回报不错。
  • 但我很想看到“正确”的答案,因为您还让 cmets 支持在类中使用私有方法;)
  • @Feroxium 我不是 PHP 开发人员,只是单元测试的倡导者,如果这是 PHP 可以提供的最好的,那么我的建议是学习 c#、java、python、ruby 或不是PHP的东西。我怀疑所有这些都和 PHP 一样收费,而函数式语言的报酬甚至更高。
猜你喜欢
  • 1970-01-01
  • 2017-06-29
  • 2012-01-18
  • 2022-08-05
  • 1970-01-01
  • 2012-05-04
  • 1970-01-01
  • 1970-01-01
  • 2013-07-01
相关资源
最近更新 更多