首页 文章

使用PHPUnit达到100%的代码覆盖率

提问于
浏览
35

我应该努力的指标,代码覆盖率报告中有一个奇怪的位,我想澄清一下 .

查看截图:

enter image description here

因为正在测试的方法的最后一行是 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];
}

有这个原因,还是一个小故障?

(是的,我通过How to get 100% Code Coverage with PHPUnit阅读,不同的情况虽然相似 . )

编辑:

通过报告跋涉,我注意到代码中其他地方的 switch 语句也是如此 . 所以这种行为至少在某种程度上是一致的,但对我来说却令人困惑 .

EDIT2:

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

4 回答

  • 36

    首先:100%代码覆盖率是 strive 的一个很好的指标 . 它's just not always achievable with a sane amount of effort and it'并不总是很重要:)

    问题来自xDebug告诉PHPUnit这行是可执行的但未涵盖 .

    对于简单的情况,xDebug可以告诉该行无法访问,因此您可以获得100%的代码覆盖率 .

    见下面的 simple example .


    2 nd更新

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

    更新(有关php 5.3.x的问题,请参见下文)

    由于您运行的是PHP 5.4和xDebug的DEV版本,我已经安装了这些并进行了测试 . 我使用您评论过的相同输出遇到了与您相同的问题 .

    如果问题来自于xDebug的 php-code-coverage (phpunit模块),我不是100%肯定 . 它也可能是xDebug开发的问题 .

    I've filed a bug with php-code-coverage我们将找出问题的来源 .


    对于PHP 5.3.x问题:

    对于更复杂的情况, CAN 失败了 .

    对于您展示的代码我可以说是"It works for me"( complex sample below ) .

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

    我已经看到它失败了当前版本,但它取决于整个 class 有时看起来如何 .

    删除 ?: 运算符和其他单行多语句事物也可能会有所帮助 .

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

    虽然 //@codeCoverageIgnoreStart//@codeCoverageIgnoreEnd 会得到这条线"covered"但它看起来真的很难看,而且通常做得比坏还好 .

    对于发生这种情况的另一种情况,请参阅以下问题和答案:

    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)
    
  • 4

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

    你真正想知道的是,“所有可执行代码都涵盖了吗?" This distinction seems silly yet leads to a solution. XDebug tracks what gets executed, well, by line number and your XDebug-based scheme thus reports ranges of executed lines. And you get the troubles discussed in this thread, including the klunky solutions of having to annotate the code with "不算我" comments, putting "}”与最后一个可执行语句在同一行上等等 . 没有程序员真的愿意这样做,更不用说维护它了 .

    如果将可执行代码定义为可以调用或由条件(编译器人称之为“基本块”)控制的代码,并且覆盖跟踪以这种方式完成,那么代码的布局和愚蠢的情况简直就是消失了 . 这种类型的测试覆盖工具收集所谓的“分支覆盖”,您可以通过执行所有可执行代码来获得或不获得100%“分支覆盖” . 此外,它会选择那些有条件的情况(使用“x?y:z”),或者你在一行中有两个常规语句(例如,

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

    由于XDebug逐行跟踪,我相信它将此视为一个声明,并且如果控制到达该行,则认为它是覆盖范围,而实际上有5个部分要进行实际测试 .

    我们PHP Test Coverage tool实现了这些想法 . 特别是,它理解返回语句后面的代码是非't executable, and it will tell you that you haven' t执行它,如果它是非空的 . 这使OP的原始问题消失了 . 不再玩游戏来获得"real"覆盖率数字 .

    与所有选择一样,有时候存在缺点 . 我们的工具有一个代码工具组件,只能在Windows下运行;已检测的PHP代码可以在任何地方运行,处理/显示由独立于平台的Java程序完成 . 因此对于OP的OSX系统来说这可能很尴尬 . 该指导员可以在支持NFS的文件系统中正常工作,因此他可以在PC上运行仪器并测量他的OSX文件 .

    有人试图提出这个特殊问题推动他的报道数量;问题是恕我直言人工,可以通过踩人工来治愈 . 有's another way to push up your numbers without writing more tests, and that'找到并删除重复的代码 . 如果删除重复项,则在效果测试(现在不存在其他副本)中测试和测试一个(非)副本的代码较少,因此更容易获得更高的数字 . 你可以read more about this here.

  • 1

    关于您的switch语句代码覆盖问题,只需添加一个“默认”情况,该情况不会执行任何操作,您将获得全面覆盖 .

  • 0

    以下是如何使switch语句100%覆盖:

    确保至少有一个测试发送不存在的案例 .

    所以,如果你有:

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

    确保至少有一个测试发送的名称既不是 terry 也不是 lucky 也不是 gerard .

相关问题