PHP 函数在多次调用时未报告为 PHP 单元完全覆盖

PHP function not reported as completely covered by PHPUnit when calling multiple times

有人可以解释为什么以下功能没有报告为 phpUnit 完全涵盖吗? 或者更好的是,解释如何将其报告为完全覆盖?

function itDoesThingsToValue()
{
    static $value = -1;
    if ($value < 0) {
        $value = 1978;
        //Do some more stuff with $value
        //But $value keeps being > 0
    }
    return $value;
}

XDebug 代码覆盖报告一次或多次调用此函数时覆盖的所有行。

PHPUnit 在调用一次时报告所覆盖的行。

多次调用它(因为不同的 PHPUnit 测试),PHPUnit 报告 {} 之间的行未被覆盖。

我知道 {} 之间的行由于静态变量 $value 而只执行一次,但恕我直言,执行的行应始终报告为已覆盖,无论它们是否在后续调用中被跳过(通过以下测试)。

我 运行 你的代码并同意,这是 st运行ge 行为,在最后显示不存在的行} 未被覆盖(83% 的覆盖率)。我在 Edge Cases 下的 PHPUnit documentation for Code Coverage 中找到了以下内容:

// Because it is "line based" and not statement base coverage
// one line will always have one coverage status
if (false) this_function_call_shows_up_as_covered();

// Due to how code coverage works internally these two lines are special.
// This line will show up as non executable
if (false)
    // This line will show up as covered because it is actually the 
    // coverage of the if statement in the line above that gets shown here!
    will_also_show_up_as_covered();

// To avoid this it is necessary that braces are used
if (false) {
    this_call_will_never_show_up_as_covered();
}

所以我将最后一个 } 移到了 return 行,它开始报告 100% 的覆盖率。

function itDoesThingsToValue(){
static $value = -1;
if ($value < 0) {
    $value = 1978;
    //Do some more stuff with $value
    //But $value keeps being > 0
}
return $value;}

我还不是很清楚,因为我不了解PHPUnit的来龙去脉,但就是这样...

在第一次单元测试期间调用我的 class 的构造函数时调用该函数。在此测试中,我声明函数被覆盖(使用@covers 注释)。

更进一步,我在数据提供者中再次调用此函数以测试另一个函数。

因为我认为我的函数已经过测试和覆盖(在构造函数测试中),所以应该保存它以便在数据提供程序中使用它。 实际上,这会导致报告的行未被覆盖。

所以我最好的猜测是数据提供者函数 运行 在单元测试之前。

编辑:

添加一些调试代码后,确实确认所有数据提供程序方法在任何测试*方法之前执行。

以下示例产生了我的问题:

public function setUp() {
    //Echo the executed testmethod name and 'starting' time into the console.
    echo $this->getName(), ' ', date('H:i:s'), chr(10), chr(13);
}

/**
* @covers MyClass::_construct
* @covers MyClass::myMethod
*/
public function testConstructor() {
    //Do some stuff
}

public function myDataProvider() {
    //Echo the 'starting' time of the data provider method in the console.
    echo 'DataProvider started executing at', ' ', date(H:i:s), chr(10), chr(13);
    $value = MyClass::myMethod; //This causes the lines not being covered
    return [[$value + 1], [$value -1]];
}

/**
* @covers MyClass::AnotherMethod
* @dataprovider myDataProvider
*/
public function testSomethingElse {
    //Do some stuff
}

控制台输出:

Testing started at 21:55 ...
DataProvider started executing at 21:55:46
PHPUnit 5.5.5 by Sebastian Bergmann and contributors.

testConstructor 21:55:47
testSomethingElse 21:55:47

我的解决方案是:

private $testClassValue;

/**
* @covers MyClass::_construct
* @covers MyClass::myMethod
*/
public function testConstructor() {
    //Do some stuff
    $this->$testClassValue = myClass::value;
}

public function myDataProvider() {
    $value = $this->$testClassValue;
    return [[$value + 1], [$value -1]];
}

/**
* @covers MyClass::AnotherMethod
* @dataprovider myDataProvider
*/
public function testSomethingElse {
    //Do some stuff
}