PHPUnit达到100%的代码覆盖率

我一直在为一个项目创build一个testing套件,虽然我意识到获得100%的覆盖率并不是应该努力指标,但是代码覆盖率报告中有一些奇怪的地方,我希望它澄清。

看截图:

在这里输入图像说明

因为被testing的方法的最后一行是return ,所以最后一行(它只是一个右括号)显示为从未执行,因此整个方法在总览中被标记为未执行。 (要么,要么我没有正确阅读报告。)

完整的方法:

 static public function &getDomain($domain = null) { $domain = $domain ?: self::domain(); if (! array_key_exists($domain, self::$domains)) { self::$domains[$domain] = new Config(); } return self::$domains[$domain]; } 

有没有这个原因,或者是一个小故障?

(是的,我阅读了如何获得PHPUnit的100%代码覆盖率 ,不同的情况虽然相似。)

编辑:

通过报告,我注意到在代码中其他地方的switch语句也是如此。 所以这个行为至less在一定程度上是一致的,但是对于我来说却是莫名其妙的。

EDIT2:

我在OS X上运行:PHPUnit 3.6.7,PHP 5.4.0RC5,XDebug 2.2.0-dev

首先:100%的代码覆盖率是一个很好的指标。 这是不是总是可以达到一个明智的努力,这并不总是重要的,这样做:)

问题来自xDebug告诉PHPUnit这行是可执行的,但没有涉及。

对于简单的情况xDebug可以告诉该行不可达,所以你得到100%的代码覆盖。

看下面的简单例子


第二次更新

现在问题是修复xDebug bugtracker所以构build新版本的xDebug将解决这些问题:)

更新(见下面的问题与PHP 5.3.x)

由于您正在运行PHP 5.4和xDebug的DEV版本,我已经安装并testing了它。 我遇到了同样的问题,因为你有同样的输出你已经评论。

如果问题来自xDebug的php-code-coverage (phpunit模块),我不是100%确定的。 这可能也是xDebug开发的一个问题。

我已经提交了一个php-code-coverage的错误,我们会找出问题的来源。


对于PHP 5.3.x的问题:

对于更复杂的情况,这可能会失败。

对于你所展示的代码,我只能说“它适用于我”( 下面的复杂示例 )。

也许更新xDebug和PHPUnit版本,然后重试。

我已经看到它与当前版本的失败,但这取决于整个class级有时看起来如何。

删除?:操作符和其他单行多语句的东西也可能有所帮助。

就我所知,xDebug中正在进行重构,以避免更多的这些情况。 xDebug曾经希望能够提供“声明范围”,并且应该修复很多这样的情况。 现在在这里没有什么可以做的

虽然//@codeCoverageIgnoreStart//@codeCoverageIgnoreEnd会得到这一行“覆盖”,它看起来真的很丑,通常做得比坏坏。

如果发生这种情况,请参阅以下问题和答案:

what-to-do-when-project-coding-standards-conflicts-with-unit-test-code-coverage


简单的例子:

 <?php class FooTest extends PHPUnit_Framework_TestCase { public function testBar() { $x = new Foo(); $this->assertSame(1, $x->bar()); } } <?php class Foo { public function bar() { return 1; } } 

生产:

 phpunit --coverage-text mep.php PHPUnit 3.6.7 by Sebastian Bergmann. . Time: 0 seconds, Memory: 3.50Mb OK (1 test, 1 assertion) Generating textual code coverage report, this may take a moment. Code Coverage Report 2012-01-10 15:54:56 Summary: Classes: 100.00% (2/2) Methods: 100.00% (1/1) Lines: 100.00% (1/1) Foo Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 1/ 1) 

复杂的例子:

 <?php require __DIR__ . '/foo.php'; class FooTest extends PHPUnit_Framework_TestCase { public function testBar() { $this->assertSame('b', Foo::getDomain('a')); $this->assertInstanceOf('Config', Foo::getDomain('foo')); } } <?php class Foo { static $domains = array('a' => 'b'); static public function &getDomain($domain = null) { $domain = $domain ?: self::domain(); if (! array_key_exists($domain, self::$domains)) { self::$domains[$domain] = new Config(); } return self::$domains[$domain]; } } class Config {} 

生产:

 PHPUnit 3.6.7 by Sebastian Bergmann. . Time: 0 seconds, Memory: 3.50Mb OK (1 test, 2 assertions) Generating textual code coverage report, this may take a moment. Code Coverage Report 2012-01-10 15:55:55 Summary: Classes: 100.00% (2/2) Methods: 100.00% (1/1) Lines: 100.00% (5/5) Foo Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 5/ 5) 

这里的大部分问题是坚持要100%执行“线路”覆盖率。 (pipe理者喜欢这个想法,这是一个他们可以理解的简单模型)。 许多行不是“可执行的”(空格,函数声明,注释,声明,“纯语法”之间的差距,例如closures开关或类声明的“}”,或者复杂的语句跨多个源代码行)。

你真正想知道的是,“所有可执行代码都覆盖了吗? 这种区别看起来很愚蠢,却导致了一个解决scheme XDebug跟踪执行的内容,以及行号,基于XDebug的scheme报告执行行的范围。 在这个线程中讨论的麻烦包括klunky解决scheme,不得不用注释代码来注释代码,把“}”和最后的可执行语句放在同一行上,等等。没有一个程序员是真的愿意这样做,更不用说维护它了。

如果将可执行代码定义为可被调用的代码或由条件(编译器称之为“基本块”)控制的代码,并且覆盖跟踪是这样完成的,那么代码的布局和愚蠢的情况简单地消失。 这种types的testing覆盖工具收集了所谓的“分支覆盖”,通过执行所有的可执行代码,您可以直接或间接地得到100%的“分支覆盖率”。 另外,它会在有条件的行内(使用“x?y:z”)或者在一行中有两个常规语句(例如,

  if (...) { if (...) stmt1; else stmt2; stmt3 } 

由于XDebug逐行跟踪,所以我认为它把它当作一个语句,并且认为它是覆盖了控制是否到达线,实际上有5个部分实际testing。

我们的PHPtesting覆盖工具实现了这些想法。 特别是,它理解返回语句之后的代码是不可执行的,并且它会告诉你,如果它是非空的,你还没有执行它。 这使OP的原始问题消失了。 没有更多的玩游戏来获得“真实”的覆盖率数字。

与所有的select一样,有时也有不利的一面。 我们的工具有一个只能在Windows下运行的代码工具组件; 仪器化的PHP代码可以在任何地方运行,并且处理/显示由独立于平台的Java程序完成。 所以这对OP的OSX系统来说可能是尴尬的。 该仪器在跨NFSfunction的文件系统上工作良好,因此他可以在PC上运行该仪器并testing他的OSX文件。

有人试图将他的覆盖率提高, 这个问题是恕我直言,人为的,可以通过人为的步骤来治愈。 还有另一种方法可以在不编写更多testing的情况下提高数字,并且可以find并删除重复的代码。 如果删除重复项,那么testing和testing一个(非)复制效果的代码就会testing(现在不存在的其他副本),所以更容易获得更高的数字。 你可以在这里阅读更多。

关于您的switch语句代码覆盖率问题,只需添加一个“默认”的情况,不做任何事情,您将得到全面覆盖。

这是做什么得到switch语句100%覆盖率:

确保至less有一个testing发送不存在的案例。

所以,如果你有:

 switch ($name) { case 'terry': return 'blah'; case 'lucky': return 'blahblah'; case 'gerard': return 'blahblah'; } 

确保至less有一个testing发送一个既不是terry也不是lucky也不是gerard的名字。