8分钟阅读

写入测试导入:首先取出最复杂的代码

一个CS博士学位,有多年的Java体验,安德烈'熟练的商业建筑师'总是希望使用他的数学/算法技巧。
被某某人翻译 Marisela Ordaz.。本文最初是写的 英语GB.

处理代码质量代码的讨论,文章和博客。人们说 - 使用技巧 测试驱动!!测试是“你必须有”的东西,以开始任何重构!所有这一切都很好,但我们在2016年,有很多产品和代码基础仍在生产中,这是创造的十,十五到二十年前。它不是秘密的,其中许多人继承了具有低测试覆盖率的代码。

虽然我总是喜欢在主边缘,甚至是血腥的,从技术世界 - 致力于新的项目和技术 - 不幸的是,它并不总是可能,我经常必须处理过时的系统。我喜欢说,当你从头开始发展时,你就是创造者,创造新事物。但是,当您使用继承的代码时,您就像一个外科医生 - 你知道系统如何运作,但如果患者从“操作”中进展顺利,你永远不会安全地了解。并且由于它是一个继承的代码,因此您可以信任许多更新的测试。这意味着,通常,第一步之一是用测试覆盖它。更精确,不仅要提供覆盖范围,而且要制定覆盖范围测试策略。

耦合和循环复杂性:智慧测试覆盖率的指标

忘记100%的覆盖范围。通过识别更有可能破裂的类来测试更智能。

基本上,我需要确定的是系统的零件(类/包)首先需要进行测试,在那里我们需要单位测试,在那里他们将更有用的质疑测试等。有很多方法可以接近这种类型的分析,我使用的那个可能不是最好的,但它类似于自动方法。一旦我的方法已经实施,就花了很少的时间来做分析,更重要的是,为继承的代码分析带来一些有趣。

这里的主要思想是分析两个度量耦合(例如,传入耦合或CA)和复杂性(例如循环复杂性)。

第一个衡量我们班级措施的课程有多少级,所以基本上告诉我们它特别是某些课程特别是系统的核心;我们必须使用我们的课程的课程越多,以测试覆盖它们就越重要。

另一方面,如果一个类非常简单(例如它只包含常数),那么如果它被系统的许多其他部分使用,则为其创建测试并不是很重要。这是第二个度量可以提供帮助的地方。如果类包含大量逻辑,则循环复杂性将很高。

相同的逻辑可以反向应用;例如,即使许多类未使用类并且只表示一个特定使用的类,如果其逻辑内部使用复杂,则覆盖它仍然有意义。

但是,有一个警告:让我们说我们有两个类 - 一个有100个CA和2的复杂性,另一种是60个交流和复杂性20.虽然第一个度量的总和更高,但我们应该覆盖第二个。这是给出的,因为许多其他类是使用的第一类,但它不是很复杂。另一方面,许多其他类也被使用的第二类,但它比第一类相对更复杂。

总结:我们需要识别具有高CA和循环复杂性的课程。在数学术语中,需要一种可用作分类的健身功能。 - F(CA,复杂性) - 值与CA和复杂性一起增加。

通常,具有两个指标之间最小差异的类应该具有测试覆盖的最高优先级。

查找为整个代码库计算CA和复杂性的工具,并提供简单的方法来以CSV格式提取此信息,证明是一个挑战。在我的搜索过程中,我发现了两个免费的工具,那么它将不公平地提及它们:

联合国Poco deMatemáticas

这里的主要问题是,我们有两个标准 - CA和循环复杂性 - 对于此,我们需要将它们组合并将它们转换为单个比例值。如果我们有一点不同的任务 - 例如,找到一个具有最糟糕的课程的课程 - 我们将有一个多选多对象的经典问题:

我们需要在所谓的帕累托前面找到一个点(上面的照片中的红色)。关于Set Pareto的有趣事情是该组上的每个点都是优化测试的解决方案。每次我们通过红线前进,我们都需要致力于我们的标准 - 如果一个人改善了更糟糕的话。这被称为标准化,最终结果取决于它的完成方式。

我们可以在这里使用许多技术。每个人都有它的利弊。但是,最受欢迎的是 EscalarizaciónLineal. y el que se basa en联合国 Punto de Referencia。线性是最简单的。我们的健身功能将被视为AC和复杂性的线性组合:

F(CA,复杂性)= a×CA + B×Complexity

Donde A Y B So Son Algunos Coefiedes。

代表我们优化问题解决方案的点在线(下面的照片中的蓝色)。精确地,它将是蓝线和帕累托的红色额头的交叉点。我们的原始问题并不完全是优化问题。但我们需要创建一个分类函数。考虑我们的分类函数的两个值,基本上在我们的范围列中的两个值。

R1 = A * CA + B *复杂性和R2 = A * CA + B *复杂性

上面写的一些公式是线程方程,甚至更多这些线是平行的。考虑更多的分类值我们将拥有更多的行,并且对于帕累托线与蓝线(虚线)相交的更多点。这些点将是对应于特定分类值的类。

不幸的是,这种方法存在问题。对于任何行(分类值),我们将以小CA和非常伟大的复杂性(反之亦然)。这立即将点放在列表上的第一个度量值之间的差异很大,这正是我们想要避免的。

另一种使标准化的方法基于参考点。引用点是具有标准的最大值的点:

(最大(CA),Max(复杂性))

健身功能将是参考点和数据点之间的距离:

F(CA,复杂性)=√((CA-CA)2 +(复杂性复杂性)2)

我们可以将这种健身功能视为与中心的圆圈。在这种情况下,RADIUS是分类的值。优化问题的解决方案将是圆圈触摸帕累托前面的点。原始问题的解决方案将是与不同圆无线电相对应的点组,如下图所示(不同类别的圆的部分显示为蓝色虚线曲线):

这种方法处理最佳极值,但仍有两个问题:首先 - 我想在接近参考点靠近参考点来更好地解决我们面临线性组合的问题。第二 - CA和循环复杂性本质上不同并具有不同的值集,因此我们需要归一化它们(例如,这两个度量的所有值为1到100)

这是我们可以申请解决第一个问题的小技巧 - 而不是查看CA和循环复杂性,我们可以看出其倒置值。在这种情况下的参考点将是(0.0)。要解决第二个问题,我们可以使用最小值来规范度量。这是你如何看待:

Complejidad Irmanizada E Invertida - 算法:

(1 + min(复杂性))/(1 +复杂性)* 100

CA Invertida Y Irmanizada - 诺卡:

(1 + MIN(CA))/(1 + CA)* 100

nota: 我添加了1以确保没有分裂0.T

以下图像显示具有反转值的图形:

Categorización决赛

我们到达最后一步 - 计算分类。如上所述,我正在使用参考点方法,因此我们需要做的就是计算矢量的长度,归一化它,并使它提升为为类创建单元测试的重要性。这是最后一个公式:

等级(算法,normica)= 100 - √(符号复合性2 + NormCA2) / √2

másestadísticas

有更多的想法是我想添加,但首先看一些统计数据。以下是耦合度量的直方图:

关于此图像的有趣事情是具有低CA(0-2)的类的数量。在0中的CA类别根本不使用或是高级服务。这些代表最终点 API.如果我们有很多,那就没关系了。但是1中的CA类是端点直接使用的类,我们将这些课程更多为最终点。这是什么意思从架构/设计的角度来看?

一般而言,这意味着我们有一个面向脚本的方法 - 我们分别为每个业务案例制作一个脚本(我们无法重用代码和业务案例非常多样化)。如果是这种情况,那么它绝对是一个 Códigoolor. 我们需要改进。但是,这意味着我们系统的凝聚力很低,在这种情况下,我们还需要改进,但在这种情况下建筑的重构。

我们可以从上面的直方图获取的附加信息是,我们可以从可用类列表中完全过滤具有低耦合(CA中的CA),用于单元测试。然而,相同的课程是集成/功能测试的好候选者。

您可以找到我在这个github存储库中使用的所有脚本和资源: ashalitkin / code-base-stats.

¿siempre funciona?

不必要。首先,一切都是静态分析,而不是执行时间。如果从许多其他类过滤了一个类,则它可以是它非常使用的信号,但它并不总是那样的。例如,我们不知道是否通过最终用户使用功能。其次,如果系统的设计和质量足够好,肯定不同的部分/层通过接口耦合,因此静态AC分析不会给我们一个真实的图像。我猜这是主要原因之一,因为CA不是一个流行的工具,就像听起来一样。幸运的是,如果您记得,我们有兴趣将此专门应用于旧和丑陋的代码基础。

一般来说,我会说运行时间分析会提供更好的结果,但不幸的是,它更昂贵,它消耗更长,并且很复杂,因此我们的方法可能是有用且更便宜的替代方案。