项目编码标准与单元测试代码覆盖率冲突时该怎么办?

时间:2011-12-04 07:23:41

标签: php coding-style phpunit

我正在开发一个学习体验的个人项目,同时也要实现一个体面的代码。这种教育的一部分,并使其成为一个体面的代码,是单元测试。我最近潜入了PHPUnit及其代码覆盖工具。

我遇到过特定实现的情况,其中使用的编码标准导致代码覆盖丢失。在这个特定的例子中,打破使用的编码标准导致代码覆盖率从88%跳到94%。

在一个方法中,我有两行,如下所示

    // .. some data validation stuff
    trigger_error('Error validating the stuff', E_USER_WARNING);
}

数据验证和内容在这里并不重要,}是。现在,当单元测试超过这行代码时,PHPUnit_Framework_Error会在}之前的行上抛出 // .. some data validation stuff trigger_error('Error validating the stuff', E_USER_WARNING);} ,因为代码从未实际继续到括号的末尾,此行永远不会被捕获代码覆盖率。

如果我这样做

PHPUnit_Framework_Error_Warning::$enabled

我的代码覆盖率增加了6%。我已经尝试将false设置为},但后来在我的终端中收到了一条丑陋的,预期的错误消息,因为我希望这个项目最终被我自己以外的人用于单元测试的错误消息是不可接受的。另外,我真的希望我的编码风格能够始终如一地实现。代码风格的违规可能会在代码的进一步删除时跳出来,这意味着我还必须添加一个可怕的注释来解释为什么大括号被移动......可能在多个地方。

我想我的问题是:

  1. 是否有一个允许使用1TBS的PHPUnit设置,并且仍然会在}之前直接被引发异常或触发错误的测试覆盖?
  2. 遵循编码标准或提高代码覆盖率更重要吗? (虽然提升实际上只是翻译额外的{{1}})

4 个答案:

答案 0 :(得分:7)

不要沉迷于一个数字。您知道报告的数字是错误的,并且您报告的覆盖范围比报告的要多,为什么要担心呢?更重要的是,您的测试涵盖所有有意义的代码,而不是您实现100%的代码覆盖率。

如果您认为编码标准很重要,并且看起来很符合,那么不要牺牲数字的可读性。

答案 1 :(得分:3)

原来的问题:

您的编码指南不应该只涉及100%的代码覆盖率。 如果您将整个应用程序编写成一行,则会得到100%,但这只是因为PHPUnit / xDebug当前报告LINE覆盖范围而不是STATEMENT覆盖率


对我来说,眼前的问题是另一个问题:

您有一种方法无条件地在最后触发错误。

所以你没有使用例外。所以问题可能是:

"Does trigger error get into the way of having 100% code coverage / complete tests"

没有。通常,trigger_error在引入的异常之前或在其遗留使用中保持一致的项目中使用。仍然php核心函数触发它们,但所有那些都有返回值来实际检查错误。

所以当使用trigger_error时,它归结为:

  • 如果要在代码中处理错误,请使用返回值
  • 如果您不想处理错误,请使用日志记录而不是触发错误

如果你有一个返回值,你可以为该方法编写有意义的测试。

一个检查trigger_error的测试用例和一个在返回值上断言的测试用例(调用具有错误抑制的方法)。

查看Example 4.12 from the phpunit docs


对我来说,这是一个你看到出错的问题,但问题不在于测试,而在于代码

您需要在测试中做一些丑陋的事情,然后开始考虑代码覆盖率,编码规则和“解决方法”。通常当我遇到这种情况时,我会从代码中退后一步,看看问题是否可以在另一个层面上解决。有时这会导致我在代码中发现设计缺陷。


回到问题:

实现100%代码覆盖率的关键在于您拥有“干净的代码”,而不是您拥有100%的代码覆盖率。努力实现它的重点在于您实际需要仔细查看在您的代码中,看看为什么在使用该类时无法访问代码行。这可能是一个设计问题。

修复这些缺陷可以轻松实现100%的覆盖率。使用黑客来获得正确的“数字”通常会比完全不关心更糟糕。你不仅要解决这个问题,还要掩盖它。

答案 2 :(得分:2)

您的覆盖错误差异来自PHPUnit及其相关的机械计算代码覆盖范围“源代码行”。这样做是没有错的,如果可以这样做的话。

因为XDebug机器只能理解它实际执行的行的行数,所以我不认为它可以“正确”,因为你的风格将“源代码行”置于无法“执行”的地方

考虑如果你写下会发生什么:

  1:   foo() {  return; 
  2:        ...1000 comment lines...
  1002:      }

XDebug看到的是在第1行执行返回,因此明确涵盖了一行(不完全正确,请参见下面的示例); PHPUnit看到的(我认为)是1002行代码。我认为如果单独运行,这将给你0.1%的覆盖率。 (在较大的程序中,你不太可能有这样的极端代码,平均值会变得更好)。

因此,您获得的报道严重依赖于您的编码风格。一个更加模糊的变化:

  1:    bar() {   if (a) { $x=2; if (b) { $y=3 } } }

由于XDebug只会跟踪行号,如果调用了bar(),则会执行第1行,并且您有一行源,因此您将获得100%的覆盖率。我相信图形显示工具也会将整条线显示为“覆盖”。但是,如果条件(a)是 false , 第二个条件根本没有机会被执行。您可以合理地争辩说只有50%的代码被覆盖(第一个是但不是第二个)。并且图形显示不正确,因为$ y = 3显示为已覆盖但未显示,因此您误导了详细信息。

为了获得更准确的数字,该工具需要知道的是单独控制的可执行代码块(在编译器文献中称为“基本块”)的数量,以及这样的数量被执行的块。在foo示例中,只有一个基本块,如果它被执行,你应该100%覆盖所有基本块,如果没有,你应该百分之零,无论你如何格式化你的代码。在条形示例中,有两个基本块(函数入口,而第二个if中的$ y = 3),如果第二个块未执行,则应获得50%的覆盖率,如果不执行,则应获得100%,无论如何它是如何格式化的。

由于我不认为PHPUnit / XDebug对基本块有任何了解,我不知道它如何能够提供更准确的数字。 (从统计数据来看,他们相当接近;你说你下降了6%,如果你的目标是达到80%,你可能需要达到74-86%,这取决于你的格式,才能获得80%'打印出来'的报道。

您使用基本块解决方案支付的价格是,覆盖范围不是“源代码行”,而是根据可执行块。我认为后者是更合适的答案, 但是你的milage可能会有所不同,老板们会因这种区别而感到困惑。

我们的PHP Test Coverage tool具有基本块的概念,通过对源代码进行编译器精确的解析来获得。它将产生更准确的数字。它的显示工具也理解你可以在一行中有多个语句,并且会正确地为已执行和未执行的部分着色。

答案 3 :(得分:2)

在改进Xdebug以将}检测为非可执行文件之前,您可以使用ignore-code-coverage comments来制作蛋糕并使用它。

    // .. some data validation stuff
    trigger_error('Error validating the stuff', E_USER_WARNING);
// @codeCoverageIgnoreStart
}
// @codeCoverageIgnoreEnd

我提交了一项功能请求,要求添加单行注释,这会让眼睛看起来更好。

} // @codeCoverageIgnore

但也许你应该遵循刺激的建议而不是过于努力达到100%的覆盖率。