我一直在为一个项目创建一个测试套件,虽然我意识到100%的覆盖率不是 指标,但是应该努力,有一点奇怪代码覆盖率报告,我想澄清一下。
见截图:
因为正在测试的方法的最后一行是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
答案 0 :(得分:36)
首先:100%代码覆盖率是 努力 的重要指标。这并不总是可以通过精力充沛的努力来实现,并且这样做并不总是很重要:)
对于简单的情况,xDebug可以告诉该行无法访问,因此您可以获得100%的代码覆盖率。
请参阅下面的简单示例。
现在问题已解决xDebug bugtracker
,因此构建新版本的xDebug将解决这些问题:)
由于您运行的是PHP 5.4和xDebug的DEV版本,我已经安装了这些并进行了测试。我使用您评论的相同输出遇到了与您相同的问题。
如果问题来自x php-code-coverage
(phpunit模块)xDebug,我不是100%肯定。它也可能是xDebug开发的一个问题。
I've filed a bug with php-code-coverage
我们会找出问题的来源。
对于更复杂的情况, CAN 会失败。
对于你展示的代码我可以说的是“它对我有用”(下面的复杂样本)。
我看到它在当前版本中失败了,但它取决于整个班级有时看起来如何。
删除?:
运算符和其他单行多语句事物也可能会有所帮助。
据我所知,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)
答案 1 :(得分:4)
这里的大部分问题是坚持要求“行”的100%执行覆盖率。 (管理者喜欢这个想法;这是一个他们可以理解的简单模型)。许多行不是“可执行的”(空格,函数声明,注释,声明,“纯语法”之间的间隙,例如,开关或类声明的结束“}”,或跨多个源行分割的复杂语句)。
您真正想知道的是,“是否涵盖了所有可执行代码?”这种区别似乎很愚蠢但却导致了解决方案。 XDebug通过行号和基于XDebug的方案跟踪执行的内容,从而报告执行行的范围。并且你得到了这个线程中讨论的麻烦,包括必须使用“不计数我”注释来注释代码的笨重的解决方案,将“}”放在与最后一个可执行语句相同的行上,等等。没有程序员真的愿意这样做,更不用说维持它了。
如果将可执行代码定义为可以调用或由条件(编译器人称之为“基本块”)控制的代码,并且覆盖跟踪以这种方式完成,那么代码的布局和愚蠢的案子简直消失了。这种类型的测试覆盖工具收集所谓的“分支覆盖”,您可以通过执行所有可执行代码来获得或不获得100%“分支覆盖”。另外,它会在一行中使用条件(使用“x?y:z”)或者在一行中有两个常规语句(例如,
)。 if (...) { if (...) stmt1; else stmt2; stmt3 }
由于XDebug逐行跟踪,我相信它会将此视为一个声明,如果控制到达该行,则认为它是覆盖范围,实际上有5个部分需要实际测试。
我们的PHP Test Coverage tool实现了这些想法。特别是,它理解return语句后面的代码不可执行,如果它是非空的,它会告诉你没有执行它。这使OP的原始问题消失了。不再玩游戏来获得“真正的”覆盖数字。
与所有选择一样,有时候存在缺点。我们的工具有一个代码工具组件,只能在Windows下运行;检测的PHP代码可以在任何地方运行,处理/显示由独立于平台的Java程序完成。所以对于OP的OSX系统来说这可能很尴尬。该指导员可以在支持NFS的文件系统中正常工作,因此他可以在PC上运行仪器并测量他的OSX文件。
这个特殊问题是由一个试图提高他的覆盖率的人提出的;问题是恕我直言人工,可以通过踩人工来治愈。还有另一种方法可以在不编写更多测试的情况下提升数字,并且可以找到并删除重复的代码。如果删除重复项,则在效果测试(现在不存在的其他副本)中测试和测试一个(非)副本的代码较少,因此更容易获得更高的数字。你可以read more about this here.
答案 2 :(得分:1)
关于您的switch语句代码覆盖率问题,只需添加一个“默认”案例,该案例不会执行任何操作,您将获得全面覆盖。
答案 3 :(得分:0)
以下是如何使switch语句100%覆盖:
确保至少有一个测试发送不存在的案例。
所以,如果你有:
switch ($name) {
case 'terry':
return 'blah';
case 'lucky':
return 'blahblah';
case 'gerard':
return 'blahblah';
}
确保至少有一项测试发送的名称既不是terry
也不是lucky
,也不是gerard
。