【问题标题】:PHPUnit Strict Mode - setUp() - CoveragePHPUnit 严格模式 - setUp() - 覆盖范围
【发布时间】:2015-04-20 12:58:40
【问题描述】:

当我遇到代码覆盖问题时,我目前开始在 PHPUnit 中使用 strict-Mode:

如果我使用setUp-方法创建我的类的新实例,__constructor-方法会列在我运行测试时所涵盖的代码覆盖范围中。

这是我的测试设置:

phpunit.config.xml

<?xml version="1.0" encoding="UTF-8"?>
<phpunit
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"
    bootstrap="../vendor/autoload.php"
    backupGlobals="false"
    backupStaticAttributes="false"
    colors="true"
    verbose="true"    
    beStrictAboutOutputDuringTests="true"
    beStrictAboutTestSize="true"
    beStrictAboutTestsThatDoNotTestAnything="true"
    beStrictAboutTodoAnnotatedTests="true"

    checkForUnintentionallyCoveredCode="true"
    processIsolation="false"
>
<testsuites>
    <testsuite name="FooTests">
        <directory suffix="Test.php">../tests</directory>
    </testsuite>
</testsuites>
<filter>
    <whitelist>
        <directory suffix=".php">../src</directory>
    </whitelist>
</filter>
<logging>
    <log type="coverage-html" target="coverage/" higlight="true" showUncoveredFiles="true"></log>    
</logging>

Foo.php

class Foo
{

    protected $_bar;

    public function __construct($bar)
    {
        $this->_bar=$bar;             //Line 10
    }                                 //Line 11

    public function getBar()
    {
        return $this->_bar;
    }

    public function getBar2()
    {
        return $this->_bar;
    }

}

和测试:FooTest.php

class FooTest extends \PHPUnit_Framework_TestCase
{

    protected $_foo;

    protected function setUp()
    {
        $this->_foo=new Foo(10);
    }

    public function testGetBar()
    {
        $this->assertSame(10, $this->_foo->getBar());
    }

    /**
     * @covers Foo::getBar2
     */
    public function testGetBar2()
    {
        $this->assertSame(10, $this->_foo->getBar2());
    }

}

如果我运行测试,我会得到以下结果:

PHPUnit 4.5.0 by Sebastian Bergmann and contributors.

Configuration read from C:\xampp\htdocs\unittest\build\phpunit.config.xml

.R

Time: 88 ms, Memory: 3.50Mb

There was 1 risky test:
1) FooTest::testGetBar2
This test executed code that is not listed as code to be covered or used:
- C:\xampp\htdocs\unittest\src\Foo.php:10
- C:\xampp\htdocs\unittest\src\Foo.php:11

OK, but incomplete, skipped, or risky tests!
Tests: 2, Assertions: 2, Risky: 1.

Generating code coverage report in HTML format ... done

只要我在测试中指定@covers,就会出现问题。

这是预期的行为吗?

我尝试过的一些事情:

  • checkForUnintentionallyCoveredCode 更改为false 显然可行,但我想使用此功能...
  • 使用processIsolation="true" 也可以。不知道为什么?
  • @covers@uses 添加到setUp() 不起作用
  • setUp() 用于测试的@covers 添加到测试中确实有效,但测试实际上并未涵盖代码。 (如果测试变得更复杂,这似乎是很多写作......)
  • 不同的phpunit-Version:我用4.34.5 进行了尝试,结果相同
  • 不同的 PHP 设置:我在带有 XAMPP 和 LinuxMint 的 Win8 上尝试了这个 - 结果相同

有没有办法从代码覆盖中删除setUp() 代码,并在测试中使用@covers 以及他们实际测试的方法?

编辑:这也会影响继承。因此,如果Bar 扩展Foo,并将参数传递给Foo::__construct,这也将出现在代码覆盖范围内——这使得为__construct 编写@covers 是一件痛苦的事情。

附加信息:

PHP 5.6.3 (cli) (built: Nov 12 2014 17:18:08)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2014 Zend Technologies
with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans

【问题讨论】:

  • 通常建议构造函数做尽可能少的工作,事实上,如果可能的话,除了将参数分配给实例变量之外,它们不应该做任何事情。一个简单的构造函数实际上并不需要进行测试,因此它对代码覆盖率报告的影响应该并不重要。至于您的情况,您可以尝试简单地使用一个空的 setup() 方法并在每个测试用例中执行 $this object = new ObjectUnderTest() 。
  • @GordonM 我唯一要测试的是,依赖项(数据库、记录器等)的类型提示是否设置正确。 __construct 方法只做赋值。

标签: php phpunit


【解决方案1】:

自从这个问题开始获得一些动力:这是我对这个问题的一种解决方案。

Foounit-test (FooTest) 将始终使用 Foo,因此我将@uses Foo 添加到课程中。

如果 protected/private 函数被 public 函数使用,这也很重要,因为否则您必须将每个 protected/private 函数添加到测试中,如果类在内部使用该函数。我什至认为如果您进行单元测试是错误的,因为单元测试必须不关心类如何“填充”,它应该只断言特定输入导致特定输出。

(另外:构造函数应该只做赋值,没有别的。)

添加@uses后,错误将消失。

(您可以将@covers Foo::_construct 添加到类中以覆盖您的构造函数的代码。)

/**
 * @uses Foo 
 * (optional)@covers Foo::__construct
 */
class FooTest extends \PHPUnit_Framework_TestCase
{
    protected $_foo;

    protected function setUp()
    {
        $this->_foo=new Foo(10);
    }

    public function testGetBar()
    {
        $this->assertSame(10, $this->_foo->getBar());
    }

    /**
     * @covers Foo::getBar2
     */
    public function testGetBar2()
    {
        $this->assertSame(10, $this->_foo->getBar2());
    }
}

【讨论】:

    【解决方案2】:

    您已使用checkForUnintentionallyCoveredCode="true" 指定了严格的覆盖范围。由于PHPUnit 4.0 PHPUnit 具有以下行为:

    处理无意覆盖的代码

    PHPUnit 4.0 可以选择性地对无意覆盖的代码进行严格处理(严格 > 覆盖模式)。启用后,PHPUnit 将无法通过使用 @covers 注释的测试并执行未使用 @covers 注释指定的代码。

    【讨论】:

    • 我完全清楚这一点。问题是:测试不执行__construct,只有setUp 执行,但是我必须在每个测试中为__construct 指定@covers,否则测试被归类为有风险的 .
    • setUp() 和 teardown() 为每个测试执行,所以从这个意义上说,它们不是测试的一部分吗?我还没有测试过,但我希望如果我使用 @expectedException 注释并在我的 setup() 或 teardown() 中抛出该异常,测试将因此通过....
    【解决方案3】:

    PHPUnit >= 6.0 上将 phpunit.xml 上的 beStrictAboutCoversAnnotation 设置为 false:

    <phpunit
         // ....     
         beStrictAboutCoversAnnotation="false"
    >
    // ....
    

    另外,你可以在没有 --strict-coverage 的情况下运行 phpunit

    更多信息Risky Tests: Unintentionally Covered Code

    【讨论】:

      【解决方案4】:

      我认为接受的答案不正确。它基本上说这个类中的任何东西都可以使用。这可能是也可能不是你想要的。特别是如果您想确保您的 testGetBar2() 方法不使用您无法使用的其他代码。

      你可以做的是ignore你的设置方法做了什么。

          /**
           * @codeCoverageIgnore
           */
          protected function setUp()
          {
              $this->_foo=new Foo(10);
          }
      
      

      这样,您在设置中添加的任何内容都不会被视为经过测试的代码,但您具体涉及的任何内容都会显示出来。

      https://phpunit.de/manual/3.7/en/code-coverage-analysis.html#code-coverage-analysis.ignoring-code-blocks

      【讨论】:

      • 在我看来,单元测试没问题,是的。您示例中的 @codeCoverageIgnore 不起作用,因为它标记了要忽略的 setUp 函数,而不是构造函数。可以将它添加到 __construct 函数中,但随后它会被所有测试忽略。
      猜你喜欢
      • 2018-05-17
      • 2014-05-30
      • 1970-01-01
      • 2012-05-18
      • 1970-01-01
      • 1970-01-01
      • 2019-06-10
      • 2020-09-12
      • 2015-02-04
      相关资源
      最近更新 更多