【问题标题】:PHPUnit and C.R.A.P indexPHPUnit 和 C.R.A.P 索引
【发布时间】:2011-12-01 23:33:38
【问题描述】:

我正在使用 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 的函数。

【问题讨论】:

  • 我们从 phpUnderControl 切换到 Jenkins。它的可配置性更高,并且正在积极开发中,并且有一个 PHP 项目的项目模板。它允许您设置将触发警告和构建失败的阈值 CRAP 值。

标签: php phpunit metrics phpundercontrol


【解决方案1】:

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

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

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

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

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

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

【讨论】:

  • 你是对的,最小值是 1 ... 似乎是 php_codebrowser 的一个错误,它会突出显示这些行,即使它们很好。见github.com/mayflowergmbh/PHP_CodeBrowser/issues/50
  • 对于三个方法的得分为 3 的课程的 CRAP 为 12 怎么样? (我认为是因为返回路径的数量)
【解决方案2】:

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

根据Alberto Savoia,CRAP索引的创建者:

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

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

【讨论】:

  • 实际上这是一个通知,但在每个 getter 和 setter 上都有一个通知很烦人
【解决方案3】:

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

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

它越高,在可测试性(包括效率)和变化点等几个方面改进它的可能性就越高。然而,直到分数超过 8000 或 9000 时,某事绝对是 CRAP 的概率才开始接近确定性。像处理解析的 XML 文档中的节点以实现无法以任何决定性方式改进的功能这样基本的事情可以轻松地将复杂性提高到数百个,同时完美无缺。

这有点像花钱。出于特定目的,您可能必须花费最低金额。它可能是一百万,也可能是一千,但无论出于何种目的,我们都倾向于假设支出越高,它的可能性就越大。但也许它需要很高,也许你正在购买一艘游艇。天真地强迫数字下降不仅会在另一个方向上犯同样的错误,而且真的很危险。由于这种灾难性的思维错误,71 人在 Grenfell Tower 被烧死或窒息,他们认为仅凭数字就能达到最好的效果。

您不应该假设减少 CRAP 会提高可测试性或可维护性。很多时候,高 CRAP 只是报告了强制性复杂性的衡量标准。您可以在技术上减少 CRAP,玩弄数字,同时降低可测试性、可维护性和可读性。你只能通过实际改进它们来改进这些东西。 CRAP 甚至不是一个可靠的改进措施。有时 CRAP 在改善后可能会下降。有时它可能会上涨。问题在于指标游戏是人们经常只是取代问题或隐藏被衡量的事物作为复杂性的指标。

一个常见的例子是使用映射而不是 switch 或 if 语句。我倾向于虔诚地自己做这件事。然而,我们忘记了我们正在取代复杂性。在这种情况下,我们可以,我们有一个带有地图实用程序的库,该实用程序得到维护并且可以依赖。如果您将该 map 函数与一些 if 语句相比,那么复杂性的总体衡量标准将是巨大的。当您没有这样的实用程序可供您使用时,您需要非常小心如何降低复杂性。例如,如果您尝试完全消除 if 语句和 for 循环,我祝您好运。

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

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

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

CRAP 可能会弄错,因为它对复杂性做出了相当幼稚的猜测。你正在接近一个悲观或最坏情况的估计。我正在查看一段代码,它的 CRAP 很高,但它无法分辨是 if($constantInScope)etc;etc;if($constantInScope)etc; 还是 if($varA)etc;etc;if($varB)etc;

如果单个函数在执行路径方面确实有数百、数千或数百万个可能的结果,那么这可能是一个很好的迹象,表明测试它存在问题。这可能是不可避免的。相反,测试可能比指示的要容易得多。圈复杂度的测量能力有限,这可能就是为什么 CRAP 似乎也包含了覆盖权重的原因。如果您对其进行了测试并获得了很多覆盖率,那么它可能并不像圈复杂度所想的那样难以测试,特别是请记住,有可能使事情无法仅根据执行路径进行全面测试,尽管非常罕见。

一个简单的例子说明为什么 CRAP 没用,展开循环并用数学语句等替换 if 语句以减少 CRAP。

【讨论】:

    猜你喜欢
    • 2019-01-25
    • 2013-07-19
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2014-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多