一、前言:关于软件质量生命周期的概念
伴随互联网、大数据、云计算与人工智能技术为代表的新一代信息技术的不断发展与广泛应用,以及敏捷开发,DevOps等各种理论方法也层不出穷,传统意义的软件质量在概念和实质内容上已经发生了许多变化。笔者作为互联网行业从业者,对于这种变化也有一些察觉,在此也斗胆提出些微看法与思考,作为抛砖引玉之举,期望能够收获各位读者的批评和指正,在此就先表示感谢了。
在前一篇关于测试右移(Shift-Right Testing)的文章中,笔者对于测试右移(Shift-Right Testing)如何落地提出了一些扩展思路(下文中简称为“测试右移探索”)。在传统计算机软件开发领域,软件生命周期SDLC是一个线性递推过程,从软件产生到报废记为同一个周期,并且整个流程呈单向流转不可逆,即从需求设计开始,历经编码、测试、验收上线、维护和下线废弃等多个流程节点.然而在当今实际的互联网项目中,基于需求不断变更成本投入项目营收等现实因素,人们更愿意聚焦在需求设计研发测试上线等这些中间的过程节点上,并且设计方法也由传统的面向过程演化为面向对象即OOP,因此往往是需要将软件生命周期(Software Life Cycle)视为从左到右的连续体或无限循环的过程,当然这不是特别新鲜的看法,只不过当下的软件研发迭代以及研发过程中,可以使用的工具比较多,让人们能够有更高的效率做事情,并且在不同环节可以用自动化的形式链接,从而达到一种无感知的丝滑效果,例如devops、RPA等,以下图为例,可以看到是一个典型的互联网产品需求研发迭代图,不同企业或许在细节上有变化,但大抵是按规律如此循环往复。或许我们可以推导并得出结论,即伴随着产品每次周期性发布上线,产品质量实际都会动态发生变化,从而引出软件质量生命周期(Software Quality Life Cycle,SQLC)的形容描述,即软件质量生命周期是由多个不同上下游紧密关联的环节组成的,它代表的软件质量是持续变化的,并且具有时间周期特性。
图1-软件质量生命周期图(Software Quality Life Cycle,SQLC)
二、软件质量生命周期的五个主要环节
一点前提说明:由于本文主题是围绕软件质量展开,因此在构建这个模型图的时候将“测试”环节放在中央位置,以便于读者理解,实际上以“需求”为中央可能更为常见。并且在这个前提假设下,也便于继续往下推导测试右移(Shift-Right Testing)以及测试左移(Shift-Left Testing)两个不同的状态。
基于第一章节流程进行的推导我们可以得出,对于软件质量最为重要影响的或者可以说决定软件质量的有五大关键环节,即需求、代码编写、测试、发布、线上等五个环节。并且这五个环节并非相对孤立,而是互相影响、互相联系。以下图为例(图2-Shift lest and Shift right from《what-is-shift-left-and-what-is-shift-right》By Saif Gunja)
图2-Shift lest and Shift right
Saif Gunja博士在他的论文《what-is-shift-left-and-what-is-shift-right》中也提到,不管是测试右移(Shift-Right Testing)或者测试左移(Shift-Left Testing),实质上要做的仍然是寻找出问题的根源,做出标记,当然在他的文章中,主要为了推行DevOps平台,他认为全面左移和右移测试的一个基本功能是自动化的全栈同时要满足可观测性和可监控。DevOps团队能够在开发的所有阶段进行端到端分析,因此可以监控所有请求和流程。从一个界面自动分析整个庞大的多云应用程序群中的所有服务,使团队能够实现自动化。测试人员可以使用脚本将部署信息和元数据推送到监控环境,以跟踪构建、修订和配置更改。姑且不论其他,从成本上看初期的投入也相当巨大,当然他的主题是讲DevOps而不是test,因此在本文不再过多引述。
三、如何度量软件生命周期质量
基于上述的五个环节,我们可以定位软件质量在这些不同的连续的环节的表现,从而得出一定的质量结论。当然我们在这里有一些前提和假定,某一环节的问题,必然会带入下一个环节,就好像由于在坏的积木上搭建了更多建筑,必然会导致建筑物累积更多安全隐患。如下图所示:
图3-缺陷引入阶段和缺陷修复成本综合估算图
上图是缺陷引入阶段和缺陷修复成本综合估算图,以红色曲线“缺陷修复成本”为例,可以看出伴随缺陷发现阶段,成本越来越高。那么我们可以反推之,缺陷发现的越早,修复成本越低,这就是测试为业务带来的额外价值。(注:该图来自于《shift-left-approach-software-testing》,参考文献1)
因此假定以测试阶段为中心,本着尽量减少成本支出的原则,左移到开发阶段,需要思考的是如何能够帮助团队在开发阶段发现更多问题。假设进一步左移到需求阶段,那么如何帮助团队完善需求,如何提前发现需求中的风险是需要深入思考的问题。由此可以得出,当测试左移到不同的软件生命周期时,发现的缺陷(数量以及深度)是完全能够折射该周期的软件质量,并且我们可以进一步得出结论,如果能够定义且评估清晰准确的缺陷的严重程度、优先级、影响范围等一系列要素参数,并充分利用这些度量数值建立观察模型,是完全能够准确的掌握每个阶段的质量走向趋势的。类似这样的事情,我们在很多软件系统中见过,例如在名为sonar的一个软件平台中,有一个叫做质量阈的聚合页,它通过展示bug数、漏洞数、代码债务周期、代码坏味道、单元测试覆盖率、代码重复率等参数进行聚合展示,从而形成对当前项目代码质量的评估,如下图所示:
图4-SonarQube质量阈示例图
SonarQube的优势在于能够相对精细化的展示当前项目的代码质量,比如伴随每次代码变更,它能够捕捉到变化,然后对静态代码进行扫描和编译,及时发现代码中的问题。下面是一个常见的Sonar静态代码规则[7]:
Change this comparison to use the equals method. type: BUG severity: MAJOR Comment: Using the equality (==) and inequality (!=) operators to compare two objects does not check to see if they have the same values. Rather it checks to see if both object references point to exactly the same object in memory. The vast majority of the time, this is not what you want to do. Use the .equals() method to compare the values of two objects or to compare a string object to a string literal. definition: 使用等于(==)和不等于(!=)运算符比较两个对象,其实不会检查它们是否具有相同的值。 相反,它只会检查两个对象引用是否指向内存中完全相同的对象。 所以绝大多数时间,这其实不是你想要的结果。 因此这里建议使用.equals()方法来比较两个对象的值,或比较字符串对象与字符串文字。下面分别展示了错误的范例和正确的范例:
advice: "不合规的范例如下:
String str1 = ""blue""; String str2 = ""blue""; String str3 = str1; if (str1 == str2) {System.out.println(""they're both 'blue'""); // this doesn't print because the objects are different } if (str1 == ""blue"") {System.out.println(""they're both 'blue'""); // this doesn't print because the objects are different } if (str1 == str3) {System.out.println(""they're the same object""); // this prints } 建议使用的合规的使用方法: String str1 = ""blue""; String str2 = ""blue""; String str3 = str1; if (str1.equals(str2)) {System.out.println(""they're both 'blue'""); // this prints } if (str1.equals(""blue"")) {System.out.println(""they're both 'blue'""); // this prints } if (str1 == str3) {System.out.println(""they're the same object""); // this still prints, but it's probably not what you meant to do }"
可以看出这种规则可以帮助程序员们减少犯错,当然我们也收到了很多程序员的吐槽,说Sonar的代码检查规则过于严苛等等,以至于他们疲于应付提高单测覆盖率,降低圈复杂度以及代码编写规范上,导致对于业务的需求无法及时响应,这也是导致许多公司在运行一段时间后不得不将Sonar束之高阁的原因之一。另外一点,Sonar集成了PMD、Findbug和Checkstyle等多个开源插件,因此想要用好Sonar平台,维护人员要对这些插件进行维护,可能需要具备“文法分析”、“编码风格”、“代码缺陷”、“语法约定”等多方面的知识储备,笔者早年曾在博客园发表的《Sonar 常用代码规则》系列文章中有过介绍,因此需要投入大量学习成本,这对于一般公司而言也是较重的负担。
四、在生命周期中,如何提高软件质量
接上一小节话题,软件质量既然能够通过一些指标化的参数进行度量,那么一定也有相应的方式对指标化数据进行提升,针对此话题我们在这一小节展开讨论。
在常规项目研发过程中,我们的软件开发和测试工程师们不断发现缺陷(defect,常常又被叫做bug)并修复缺陷,并通过循环往复此过程的方式,最终当发现的缺陷数不再上升然后将剩余的缺陷逐一解决清零(迫于上线时间周期压力,项目组有时也会留一些无足轻重的缺陷在后续迭代中进行修复),这就是传统研发模式中对质量的定义和控制方式。
4.1 发现需求中的疑点
按照测试左移(Shift-Left Testing)的说法,在上一章节中我们初步探讨过在需求阶段,需要帮助产品经理发现需求本身存在的缺陷,在此阶段也是研发和测试人员能够较早参与的正式的项目环节,一般来说我们建议参与项目的队员们能够积极参与需求全过程的讨论,理解项目完整故事,明确项目内容和边界,对项目可行性、项目合理性、项目的政策和合规风险、人力和时间成本给出初步的建议。
4.2 拆解需求中的缺陷
因此据上述部分而言,我们可以推导详细扩展到一些具体的例子,例如以下几个点是可以参照的范本:1、项目范围是否确定,意思是说项目的边界是否能确定,从哪里开始,到哪里结束?与以往的项目背景是否有耦合,与下游项目如何接驳;这是为了对项目边界做初步判断,意味着测试行为应该从哪里开始和到哪里算做结束。2、假如该项目是需要与外部团队合作的项目,要注意对方是否有测试环境可以联调,对方的测试数据是否完备是否能够打通,这方面涉及到测试环境全流程是否能走通;3、涉及到其他团队的内容,譬如功能接口、项目排期等等,因此防止遗漏内容以及可行性风险,必须有对方代表参与评审;4、政策以及合规风险,如未经过需求讨论,则需要在评审阶段思考政策风险;5、初步了解技术的可行性方案,以及初步设计测试方案,例如软件的可测性、兼容性、软件性能等。(限于篇幅,当然这里没有穷举了所有的可能性)
4.3 对需求中的缺陷进行指标化
根据上述在需求阶段发现的这些问题,一样可以归并为缺陷系统中,按照一定的优先级对其进行格式化,然后纳入统一管理体系并且成为关乎软件质量生命周期的一个个衡量的指标化数据,就像上一小节中的SonarQube质量阈指标(事实上关于本文中指标化的很多思路灵感是来自于SonarQube系统,在此表示感谢)。针对这些能够量化的指标们进行定点修复,就是在不断优化和提高软件质量,那么软件质量生命周期的指数就应该相应产生向良好趋势方向的变化,这个不断循环往复的过程看上去和本小节第一段落提到的传统研发模式极其类似了,因此看起来这些底层逻辑道理都是相通的。
4.4 将指标转化为测试用例
实际上在“测试右移探索”一文中,笔者对于产品线上阶段的用户体验问题发现机制,包括问题类型,种类,当然最重要是对于线上软件质量的量化方法进行了一种较为深层次的探讨,建立了许多标准项,然后把标准项又转化为测试场景(test scenario),然后进一步也演示了如何把测试场景(test scenario)转化成为具体的测试用例(test case),因此在本文中就不再对此块内容展开更多描述了,感兴趣的读者可以移步到该文中探讨学习。
在本章节中,笔者针对“需求”阶段详细解构并探讨了指标化的过程,对于“线上”阶段在另一篇“测试右移探索”一文中也进行了较为深入的探索,博文篇幅有限对于剩下的三个阶段就不再展开了,有兴趣的读者可以私下沟通交流。
五、关于软件质量生命周期的发散的反思
在文章的最后,希望能跟读者们一起做一些发散思考。如果我们从闭环的角度进行观察,会发现该生命周期与PDCA循环很像,因此同样面临的一个困境是创新不足。因为过于流程化的设计将会导致人们形成惯性思维,周而复始的按照流程化工作,难以跳脱出来观察全局,从而缺乏创新性,或许需要思考如何引入外部变量,进而打破这种平衡。
另外一点,所谓右移在发布阶段,研发团队一般都会建立一些监控手段,例如对应系统层(cpu、memery)、应用层(response time)、业务层(user registration、deal volume),分别建立相应的监控措施,因此在开发阶段和测试阶段是否也关注了这些环节,在不考虑成本的前提下,是否可以建立同样的观察机制和报警机制,如果资源有限又能做到何种程度。
最后的最后,占用一点篇幅,在此由衷感谢Saif Gunja和Arthur Hicken两位软件行业前辈,这篇文字也是从他们的博客汲取了许多灵感和知识。另外,当然文章中必定会有许多谬误之处,请读者不吝指正,在此也一并再次表示感激。
作者:William Wang