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
的名字。