PHPUnit 和 C.R.A.P 索引

PHPUnit and C.R.A.P index

提问人:RageZ 提问时间:10/6/2011 最后编辑:casperOneRageZ 更新时间:10/16/2019 访问量:12782

问:

我正在使用 php undercontrol,代码浏览器在每个 setter/getter 上报告一些 CRAP 索引错误,即这样的代码

public function getFoo()
{
    return $this->_foo;
}

getter/setter 包含在单元测试中,复杂性为零,因为没有 if/for/switch/foreach。那么为什么我得到该代码的 CRAP 索引为 1???

PS:自我回答可能是因为复杂性没有,但我的主要问题是每个 getter/setter 都会因为 CRAP 索引而生成警告,所以无论如何都可以告诉 phpunit/php 代码覆盖率使 CRAP 等于 0 对于复杂度指数为 0 的函数。

php phpunit 指标 phpUndercontrol

评论

0赞 David Harkness 10/7/2011
我们从 phpUnderControl 切换到 Jenkins。它的可配置性要高得多,并且正在积极开发中,并且有一个用于 PHP 项目的项目模板。它允许您设置阈值 CRAP 值,这些值将触发警告和生成失败。

答:

1赞 Paul 10/6/2011 #1

这真的是一个警告吗?通常,警告的阈值设置远高于 1(可能在 30 左右)。这里有一个很好的 SO 帖子,展示了如何计算数字。在我的 phpunit 设置中似乎有一些硬编码值,用于 CRAP 为 30。

根据 CRAP 指数的创建者 Alberto Savoia 的说法:

“C.R.A.P.(变更风险分析和预测)指数旨在分析和预测维护现有代码体所需的工作量、痛苦和时间。”

最小 CRAP 数将是 100% 覆盖率代码的圈复杂度。这个想法是,对复杂代码的更改比对简单代码的更改更容易产生问题。

评论

0赞 RageZ 10/6/2011
实际上,这是一个通知,但对每个 getter 和 setter 都有通知很烦人
32赞 case nelson 10/6/2011 #2

最低 CRAP 分数为 1,而不是 0。这是因为 CRAP 的算法是

CRAP(m) = comp(m)^2 * (1 – cov(m)/100)^3 + comp(m)

函数的最小圈复杂度 (comp) 值为 1。所以问题不在于 phpunit,而在于将 CRAP 标记为 1 的问题。

一般来说,您希望将 CRAP 阈值设置在 5 左右,或者更低的位置,并且您不妨使用一个简单的代码覆盖率指标(并争取 100%),因为复杂性因素几乎没有影响。>= 30 的 CRAP 意味着再多的测试也无法使您的方法变得糟糕。

圈复杂度通常(但有多个定义)可以手动计算为:

  • 为函数调用添加 1 分
  • 每个循环加 1 分
  • 每个分支加 1 分

评论

0赞 RageZ 10/6/2011
你是对的,最小值是 1 ...似乎是php_codebrowser的一个错误,即使它们很好,也会突出显示这些线条。查看 github.com/mayflowergmbh/PHP_CodeBrowser/issues/50
0赞 MrMesees 1/2/2017
对于三种方法得分为 3 的班级来说,CRAP 为 12 怎么办?(我认为是因为返回路径的数量)
0赞 Xavi Montero 8/10/2022
如果完全覆盖@MrMesees类,12 的 CRAP 是可以容忍的。如果你是 12 岁,而且班级没有 100% 覆盖,再覆盖一点,你可能会下降到 10 或 8。尽管如此,如果经过全面测试并且您得到 12 个,您需要看看这是否合理,或者您的班级正在扮演两个不同的角色,这应该“建议”您分成 2 个单独的班级。相反,如果你的类来自遗留,并且得到了 40 或 80,只需尽快重构即可。甚至未经测试的类报告了 200 甚至 400 个,也要“立即”重构。
2赞 jgmjgm 6/27/2019 #3

CRAP 只是一个指标。就其本身而言,它和“一根绳子有多长”一样有用,除了是一个答案不确定的问题之外,它还是一个答案不确定的问题。

如果你知道它在测量什么,那么你可以把它作为一个非常基本的复杂性指标。为了更多地使用它,您需要相当多的经验来比较实现。之后,理想情况下,您需要在相同事物的实现之间进行比较。在那之后,你需要密切了解正在测试的代码,如果你这样做了,你很可能比 CRAP 分数有更好的洞察力。

它越高,在可测试性(包括效率)和变化点等几个方面改进的可能性就越大。然而,直到超过 8000 或 9000 的分数,绝对 CRAP 的可能性才开始接近确定性。像处理已解析的 XML 文档中的节点这样基本的事情,对于一个无法以任何决定性方式改进的功能,很容易将复杂性提高到数百个,同时又完全没问题。

这有点像花钱。对于给定的目的,您可能需要花费最低金额。它可能是一百万,也可能是一千,但无论目的如何,我们倾向于假设支出越高,它就越有可能过度。但也许它需要很高,也许你正在购买一艘游艇。天真地强迫数字下降不仅在另一个方向上犯了同样的错误,而且是真正危险的。由于这种灾难性的思维错误,71人在格伦费尔塔被烧死或窒息,他们认为仅凭数字就可以最好地实现某些目标。

您不应认为减少 CRAP 可以提高可测试性或可维护性。很多时候,高 CRAP 只是报告强制性复杂性的度量。从技术上讲,您可以减少 CRAP,玩弄数字,同时降低可测试性、可维护性和可读性。你只能通过实际改进它们来改进它们。CRAP 甚至不是衡量改进的可靠指标。有时,CRAP 可能会在改进后下降。有时它可能会上升。问题在于,在度量游戏中,人们往往只是将问题置换掉,或者将被测量的事物隐藏起来,作为复杂性的指标。

一个常见的示例是使用 map 而不是 switch 或 if 语句。我倾向于自己虔诚地做这件事。然而,我们忘记了我们正在取代复杂性。在这种情况下,我们可以,我们有一个带有地图实用程序的库,该实用程序已维护并且可以依赖。如果将该映射函数与几个 if 语句进行比较,则复杂性的总体度量将达到顶峰。当您没有这样的实用程序时,您需要非常小心地降低复杂性。例如,如果你试图完全消除 if 语句和 for 循环,祝你好运。

如果可以提高测试速度,则圈复杂度确实会很好地反映出来。这适用于函数中有两个 if 语句等情况。如果第二个所依赖的状态因第一个是否匹配而有所不同,则必须冗余运行第一个(4 次而不是 2 次)。当您组合代码时,可能的排列通常会以非线性方式增加。如果你有八个函数接受一个布尔值并返回一个布尔值,那么你可以单独测试每个函数以获得 8 乘以 2(布尔值有两个可能的输入值)测试(16 个测试)。但是,如果将所有这些函数组合在一起,则有 256 种不同的输入可能组合。圈复杂度有助于指出可能出现这种情况的情况。

它还提供了一些指示,表明您真正需要多少次函数时间参数的测试。如果你有一个函数,有一个布尔参数和一个基于它的 if 语句,那么你至少需要两次测试才能获得至少完整的代码覆盖率。两个布尔参数和两个 if 语句,然后是四个或三个,具体取决于 if 是否嵌套。在最坏的情况下,如果在 if 之后添加,则可能需要测试的组合数将增加 2 的幂。

这可能会产生冲突,迫使您过早地对代码进行分段,因为在运行时,您可能永远不会遇到该问题。在测试开始消耗大量资源或实际上开始变得不成比例地笨拙之前,您通常不必担心这一点。在这种情况下,你不会依赖 CRAP,而是依赖对代码执行和基准的理解。

CRAP can get it wrong as it makes a fairly naive guess about complexity. You're getting closer to a pessimistic or worst case estimate with it. I'm looking at a piece of code that's got a high CRAP but it's not able to tell between having or .if($constantInScope)etc;etc;if($constantInScope)etc;if($varA)etc;etc;if($varB)etc;

If a single function genuinely has hundreds, thousands or millions of possible outcomes in terms of execution path then that is probably a good sign that there are issues testing it. that might be inescapable. Conversely, it might be far easier to test than indicated. The limited ability for cyclomatic complexity to measure that might be why CRAP also appears to include a counter weight of coverage. If you tested it and got a lot of coverage then it probably isn't that hard to test as the cyclomatic complexity thought, especially keeping in mind that it's possible to make things impossible to fully test in terms of execution paths alone by necessity, albeit very rare.

A simple example of why CRAP is useless, unroll your loops and replace if statements with mathematical statements and the like to reduce CRAP.