软件质量指标自动度量方法
星期一, 五月 17, 2021
通过设定衡量代码质量的八个度量项来对软件的质量进行量化打分,其设定度量项的标准参考了定义软件质量的ISO25010标准。这篇文章结合鸿渐科技团队多年的实践,将给大家介绍一下如何通过ISO25010标准来制定以下的质量指标。
质量指标
30年多年前,软件工程师Barry Boehm已经发现,如果在软件发布后发现缺陷,修复缺陷的成本会成倍增加。因此,如果能够在软件发布前有一种方法来衡量软件的代码质量,它将潜在地节约大量的成本。一个定义软件代码质量的ISO25010应运而生,这个标准定义了八个主要质量指标和许多子指标。八个主要质量指标为:
ISO25010标准有助于在软件初期阶段评估质量。然而,它有两个主要缺点:
为了对软件质量进行系统的评估,经过多年的行业经验积累,制定出可以进行量化处理的八个度量项,它们分别是:
这八个度量项基于ISO25010制定,其量化数值和软件质量属性有一定的映射关系,具体如下。
1. 代码覆盖率
在软件工程师将他们的代码移交到软件开发周期的下一个阶段之前,他们通常做一些单元测试。这些是小规模的自动化测试,可以检查程序的特定部分,例如单个函数,然后将这些自动化测试的实际结果与预期结果进行比较。单元测试是用来检查程序能否实现设计想达到的目的最低标准的一种有效的测试方法。代码覆盖率表示在单元测试运行期间,代码中有多少行代码或可执行分支被测试。覆盖率越低,所执行的单元测试的质量就越低。代码覆盖率是“功能适用性”和“可靠性”的一个度量指标。
下面的C#代码展示了用代码覆盖率检测工具输出的一个简单示例。每一行颜色为“绿色”的代码都经过至少一次测试,而“红色”行的代码没用经过任何测试。
代码覆盖率测试工具的输出显示,除第37行外,此代码示例中的所有行都由(单元)测试覆盖。
2. 抽象解释
现在有一种通过运行抽象解释工具(也称为深流分析工具)来检测软件程序中可能存在的“可靠性”问题的新技术。这些工具能够自动检测与程序控制流程相关的各种编程错误。例如空指针解引用、除零和未关闭的数据库连接。这些工具的优点是它们在不实际运行程序的情况下就能产生结果,这是通过以计算程序所有可能的路径来完成的。抽象解释发现的错误是严重的编程错误,可能导致崩溃。这个度量项和程序“可靠性”属性息息相关。关于抽象解释问题的一个简单示例展示在下面的Java代码中。
抽象解释工具将在第228行标记一个可能出现的空指针解引用缺陷,因为函数“get Order”会在订单没有有效日期的情况下返回NULL。如果发生这种情况,将抛出异常,可能导致程序崩溃。
3. 圈复杂度
圈复杂度是最经典的软件度量项之一。圈复杂度在数量上表现为独立现行路径条数。例如,每个“if”语句就会添加了一条额外的代码路径。圈复杂度越高,程序代码的判断逻辑就越复杂。此外,路径越多,就需要编写更多的测试用例来实现更高的代码覆盖率。每个函数的平均圈复杂度是一个指标,可以比较程序之间的复杂性。圈复杂度在一定程度上展示了程序代码的“可维护性”。下面以一段C#代码为例展示如何计算圈复杂度。
函数“get Value”在第123行的圈复杂度为2(因为包含一条“then”路径和一条“else”路径”)。
4. 编译器警告
为了在计算机上执行软件程序,首先需要经过编译或解释。编译器或解释器会生成错误和警告而且错误必须修复,否则程序无法运行。警告虽然不一定需要解决,但是一些编译器警告表明程序存在严重缺陷。留下这些未解决的问题可能会影响代码的“可靠性”。除此之外,大多数编译器警告还体现了“可移植性”问题。因此,这个度量项和软件程序的“可移植性”也有很强的关联。下面是关于编译器警告在C语言代码片段的一个简单示例。
大多数编译器会在第32行的if条件下发出警告(可能是为了进行比较的原因)。
5. 编码标准
软件维护是软件工程师最耗时的任务之一。其原因之一是经过多次更新后,软件工程师很难理解程序代码编写原本的意图。降低软件维护成本的一种方法是引入编码标准。编码标准是代码工程师应该遵循的规则。这些编码规则涉及已知的语言缺陷、要避免的代码构造,还涉及命名约定和程序布局。由于编码标准通常包含许多不同的规则,所以它们可以反应大多数代码质量属性问题。大多数规则涉及“可维护性”和“可靠性”,但也有可用于“可移植性”和“效率”的规则。下面是一个违反编码标准的示例。
任何C编码标准都不推荐第36行使用的goto语句。
6. 重复代码
有些时候软件工程师会复制大量现成代码并对其进行一些小的修改而不是重新编写。大量重复代码的缺点是,如果出于某些原因(修复bug或添加丢失的功能)必须更改代码的一部分,那么其他重复的代码也很可能需要更改。一旦有所疏忽,重复的大量代码将产生巨大的工作量。这非常影响程序的“可维护性”。
7. 扇出
软件程序通常是由模块或组件构造的。这些模块和组件存在层次调用的情况。扇出表示某个模块使用的下级模块的个数。如果模块需要许多其他模块才能正确运行(高扇出),那么模块之间就有很高的相互依赖性,这使得代码更难修改。因此,扇出在一定程度上反映了软件程序的“可维护性”。下面的Java代码显示了一个高扇出的示例。
在这段代码中,我们采用了扇出的简单定义来度量import语句。因此,上面Java文件的扇出数量是16。
8. 安全
软件的安全性反应了在未获得授权的数据访问时软件程序有多容易被攻击,以及利用安全漏洞对软件进行更改的难易程度。这种安全漏洞的例子有缓冲区溢出(让程序崩溃)和敏感数据的暴露(从而给用户提供信息以获得未经授权的访问)。下面的C代码给出了一个安全泄漏的示例。
在第319行,一个非常长的字符串被写入一个名为“buf”的数组,该数组只能容纳8个字符。不适合“buf”的字符被保存在其他地方,可能会覆盖应该执行程序的代码。通过利用这个漏洞,攻击者可以运行另一个程序,而不是运行的本来的程序。修正后的例子是下图。
通过使用“snprintf”而不是“sprintf”,写入缓冲区的字符数受到第二个参数的限制。
以上就是给出的八个度量项,从上文可以发现一些度量项可以很容易地映射某些质量属性。例如,如果一个文件的代码重复率为0%,那么这被认为是质量较高的一段代码,而如果重复率是50%,那会被认为是糟糕的编程。然而,对于八个度量项中的四个,“抽象解释”、“编译器警告”、“编码标准”和“安全”并没有和质量属性间存在非常明确的对应关系。例如,如果代码中有3000个编码标准问题,那么这段编码的质量高低还取决于以下3个附加因素:
1. 测量了多少编码规则?如果一个编码标准比另一个编码标准有更多的规则,那么违规的可能性就会更高。但这并不意味着该代码的代码质量较低。
2. 违反的规则的严重程度是多少?如果只违反了不重要的规则,那么代码质量就会比同样违规数量的其他代码更好。
3. 代码的数量级是多少?如果在一个由1000万行代码组成的系统中有3000起违反代码规则事件,那么与一个只有1000行代码的系统相比,情况就显得不那么严重了。
为了解决这一问题,引入了“加权因子” (compliance factor) 的概念。加权因子表示软件代码在多大程度上符合某一组规则。例如,这可能是一组编译器警告或一组安全规则。
具体的计算公式如下:
对此公式的详细解释大家可以参看文末相关文献链接。许多项目中使用这一定义已有20多年,在实践中效果显著。通过行业经验确定了度量项在软件质量属性中所占权重大小,然后分别计算每个度量项分数后进行加权汇总,得到反应软件质量等级和评分的一个检测报告。
质量指标是一种非常实用的方法,可以在软件程序发布前甚至在系统测试之前对软件代码的质量进行量化概述。该指标结合了最著名的代码质量度量项,通过公司现有的代码检测工具定义了它们是如何测量的,以及如何判断质量的高低。按照得到的分数,将软件系统依次标记为A(优秀质量)到F(质量差)多个不同的质量等级。
鸿渐科技现有的“源代码检测系统”可以对代码的圈复杂度,扇入扇出和重复代码比例做量化分析,同时,参考众多国内外顶会文章的相关指标量化的设计也在积极努力的开发中。