【问题标题】:How to reach 100% Code Coverage when not testing protected methods不测试受保护方法时如何达到 100% 的代码覆盖率
【发布时间】:2016-03-21 20:47:10
【问题描述】:

这有意义吗:

由于我们通常只想测试(类的)API 而不是我们通常不需要/不想测试受保护方法的实现细节。由于我们不希望仅仅因为我们不测试实现细节而降低代码覆盖率,因此我们应该为那些受保护的方法使用 @codeCoverageIgnore 注释。

【问题讨论】:

  • 我很好奇这里的最佳实践。在我工作的地方,由于你提到的事情,我们只是没有要求 100% 的覆盖率。相反,如果覆盖率下降超过 X%,我们会在 CI 中制定规则以使构建失败

标签: php unit-testing phpunit


【解决方案1】:

通过公共 API 测试您的私有和受保护方法。

“不要测试你的私有”规则并不意味着你不应该测试私有方法提供的行为。这意味着您应该通过公共方法测试该行为。如果您这样做,您将获得稍后更改实现的灵活性(即创建不同的私有方法,或内联它们)。

显然,您将为正在测试的单个方法编写多个测试用例。确保您命名这些方法以清楚地说明您的期望。例如:

  • test_it_reverses_the_name()
  • test_it_lowercases_characters_in_the_reversed_name()
  • test_it_throws_an_exception_if_name_is_missing()

请注意,如果您要测试公共方法,通常不会从创建私有方法开始。您宁愿将它们提取为重构步骤。稍后您可能还决定内联这些方法而不需要更改测试。那是因为你只是在测试公众的行为。

无需使用@codeCoverageIgnore@covers。你会自欺欺人的。

示例

Foo.php:

<?php

class Foo
{
    private $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function getReversedName()
    {
        $this->foo();

        return strrev($this->name);
    }

    protected function foo()
    {
        $foo = true;
    }
}

FooTest.php:

<?php

class FooTest extends \PHPUnit_Framework_TestCase
{
    public function test_it_reverses_the_name()
    {
        $foo = new Foo('test');

        $this->assertSame('tset', $foo->getReversedName());
    }
}

phpunit.xml.dist:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
     backupGlobals="false"
     colors="true"
     bootstrap="vendor/autoload.php"
>
    <php>
        <ini name="error_reporting" value="-1" />
    </php>

    <testsuites>
        <testsuite name="Project Test Suite">
            <directory>tests</directory>
        </testsuite>
    </testsuites>

    <logging>
        <log type="coverage-html" target="build/coverage"/>
    </logging>
</phpunit>

【讨论】:

  • 我同意,但没有使用@codeCoverageIgnore@covers 我体验到代码覆盖率将下降并且受保护/私有方法被标记为未经测试(红色)。请查看我原始问题中的屏幕截图(尤其是foo() 方法)。你是否也遇到过这种情况,你是如何应对的?
  • 不,这不是我的经验。根据我的经验,如果您的测试调用了一个调用私有方法的方法,那么两者都将被覆盖。确保您的私有代码在测试中实际到达。
  • 另外,我注意到您的私有方法只为局部变量赋值。计算覆盖率的方式有问题,有时不包括右括号。由于您只有一种行方法,因此那里可能出了点问题。如果覆盖范围发生任何变化,请尝试从这些方法返回以进行验证。
【解决方案2】:

如果一个测试涵盖多个类方法,您可以使用covers annotation as described in the doc here。例如:

/**
 * @covers Foo::foo
 * @covers Foo::bar
 */
public function testReversed()
{
    $this->assertEquals(0, $this->foo->getReversedName());
}

希望有帮助

【讨论】:

  • 谢谢Matteo,你已经在stackoverflow.com/a/31587557/999596 中提出了这个建议,但我认为@SamHolder 在他的回答中有一个很好的观点(那里),你的测试可能对实现了解太多,你可能经常有当您只更改实现而不更改 API 本身时,更改这些 @covers 注释。你怎么看?
猜你喜欢
  • 2015-10-13
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-29
  • 2013-07-01
  • 2023-03-24
相关资源
最近更新 更多