我曾与一些资历非常高但毫无实际经验的人共事过,也曾与一些只有很少或根本没有资历但才华横溢的工程师一起工作过,我也曾经不得已跟一些并不想用心做事、也对学习新东西丝毫不感兴趣的人共事过。如果说我们这个职业是一张纸,那么这些人就好比纸上的污点。软件开发业的低劣性不能完全怪罪于那些无知的经理、狡猾的市场营销人员以及总是急不可耐的用户,实际上很大程度上要归咎于这个行业的某些从业人员,他们应该去从事一些即使玩忽职守也不会造成像软件业里这样大的危害的行当,而不应该混迹于这个聚集着人类想象力的最复杂的创造性的行业。
-- MatthewWilson
最近,有同事请教了我一些关于软件质量的相关问题,很抱歉我没能给出一个较好的答案。主要原因是,这两年来,一直忙碌在基本知识的学习总结上,虽然也接触了很多这方面的内容,却没有一个很好的总结以及记录,对此深表惭愧。
这一次的项目的启动会议上,听说了小韩的这么一句话,大概是这样子的,“我们作为一个互联网公司,不但要做一个优秀的产品,还要从产品上去教育群众,引导社会”。加上这段时间看到前面Matthew Wilson的这段话,觉得自己作为这个行业的一员,肩负着重大的责任。
所以,借此机会,对这两年来关于亲身经历过的软件质量管理进行总结。另外,声明一下,首先,这只是我对这方面的一些思考,可能跟其他人的想法不同,如果有其他想法的同学们,我们可以进行讨论学习,以求共同进步;其次,个人观点难免会和其他人发生冲突,如果对读者们造成了冒犯,还请多多原谅和多多指教;最后,我是非常喜欢讨论的一个人,因为每次讨论都会有丰富的收获,如果有同学们进行讨论学习,我非常高兴!
这部分跟整个主题(PerfectC++)可能不太符合,但是每一门语言都应该有对应的质量管理方式,毕竟每个软件都不能保证没有Bug,所以软件质量也是重中之重,根本之根本。所以我依旧将其列入Perfect C++的一部分,但是内容应该也适用于大部分语言。
说道软件质量,首先是如何进行衡量呢?我想大部分人想到的最简单的一个衡量标准就是:这个软件有多少个Bug?
这样简单的标准,其实是错误的。第一,软件的Bug是不能进行统计的,因为在不同场合下,不同情境下,都可能存在Bug,但是这个数量却是无法衡量的。第二,质量不能单靠“错误”来衡量,例如说软件的执行效率等等,也应该作为标准之一。
说到这部分,我也只能引用《代码大全》中的相关部分了(第20章软件质量概述)。其中,软件质量从外在特征,即用户角度来说,包括:正确性、可用性、效率、可靠性、完整性、适应性、精确性和健壮性;从内在特征,即代码角度来说,包括:可维护性、灵活性、可移植性、可重用性、可读性、可测试性和可理解性等方面。
关于外在特征,是我们经常关注的部分,因为站在用户的角度来说,这就是用户最关注的部分,用户关心的,自然是我们最看重的。但是软件质量并不能将这些细节都进行多度的处理,过于关注某个细节,就会对其他方面产生一定的影响,大概情况如下表:
|
正确性 |
可用性 |
效率 |
可靠性 |
完整性 |
适应性 |
精确性 |
健壮性 |
正确性 |
↑ |
|
↑ |
↑ |
|
|
↑ |
↓ |
可用性 |
|
↑ |
|
|
|
↑ |
↑ |
|
效率 |
↓ |
|
↑ |
↓ |
↓ |
↓ |
↓ |
|
可靠性 |
↑ |
|
|
↑ |
↑ |
|
↑ |
↓ |
完整性 |
|
|
↓ |
↑ |
↑ |
|
|
|
适应性 |
|
|
|
|
↓ |
↑ |
|
↑ |
精确性 |
↑ |
|
↓ |
↑ |
|
↓ |
↑ |
↓ |
健壮性 |
↓ |
↑ |
↓ |
↓ |
↓ |
↑ |
↓ |
↑ |
当然这只是一些典型关系,在不同的项目中,可能影响恰好是相反的。
作为一名开发人员,应该对内在特征进行思考。据我所知,其实大部分人是没有关注这部分的,而这部分的影响又是非常大的。而这部分又是外在特征的根本之所在,从我的角度来理解,内在特征管理到位,会大幅度提高软件质量。
说了这么多,可能有很多人不是很在意软件质量这个话题,但是想一下,一个人每天平均写多少行代码呢?其他时间都在干什么呢?我想大部分时间都是在调试,定位Bug,解决,再测试吧。
提高生产效率和改善质量的最佳途径就是减少花在这种代码上的返工时间,无论返工的代码是由需求、设计改变还是调试引起的。
另外,要为软件添加一个功能,时间消耗又是多少呢?我想一个与整体毫无关联的功能添加要比在一个复杂逻辑的功能里面追加要简单的多吧。
所以,效果明显的缩短开发周期的办法就是改善产品的质量,由此减少花费在调试和软件返工上的时间。
当然,上面这两点都是在大量数据上得到的结论,具体请参考《代码大全》第20.5章节:软件质量的普遍原理。
总说些不实用的话,很没意思,所以赶紧跳过前面的凉菜,开始上硬菜!
既然软件质量分为这么多方面,那到底该如何改善呢?改善的方法主要有:
1> 确定明确的软件质量目标。首先要有明确的目标,如果目标不制定,那就如沙漠中行军,最后也是全部灭亡的效果。
2> 确定保证这些目标要做的工作。其次,为了达到这些目标应该展开那些工作?只有目标,没有工作计划,相当于在原地休息,效果也是一样的。
3> 确定明确的测试策略。测试不能改善软件质量(这个跟现在工作的状况恰好相反,真是让我无力吐槽),根据测试结果来评估和改善软件质量,就是大海捞针而已,捞到捞不到只能看运气,并且还会导致测试人员苦不堪言。测试人员一定要提出明确的测试策略,不能将“测试”作为改善软件质量的首要方法。
4> 确定明确的需求定义。需求定义一定要明确,很多情况下,需求人员定义的跟用户想得到的恰好相反,这样的开发都是无用功。
5> 确定明确的开发工程指南。开发工程指南包括上面各个方面的定义,应当控制软件的技术特征,贯彻所有开发活动之中,例如说代码规范就是一个典型的代表。
6> 代码复查。这个工作非常重要,不但可以发现大量的问题,还可以一定程度上提高人员的技术质量,但也有一些复查很不到位,过分关注细节,投入成本过高,收获却很少。
下面是一些常用的技术和对应检出Bug的检测率,可以有一个直观的了解。
检错措施 |
最低检出率 |
典型检出率 |
最好检出率 |
非正式设计复查 |
25% |
35% |
40% |
正式设计复查 |
45% |
55% |
65% |
非正式代码复查 |
20% |
25% |
35% |
正式代码复查 |
45% |
60% |
75% |
个人代码复查 |
20% |
40% |
60% |
单元测试 |
15% |
30% |
50% |
组件测试 |
20% |
30% |
35% |
集成测试 |
25% |
35% |
40% |
回归测试 |
15% |
25% |
30% |
系统测试 |
25% |
40% |
55% |
小规模Beta测试(10人以下) |
25% |
35% |
40% |
大规模Beta测试(1000人以上) |
60% |
75% |
85% |
上面的这些数据,应该是每一个测试人员都应该了解的。测试经理也并不是只是简简单单安排估算一下测试用例条数,分配用例编写,安排人员进行测试,提交Bug而已。按照我个人的理解,确定软件质量才是一个测试经理首要任务,测试只不过是其中最最基本的之一。如果单靠测试,那么每个刚毕业的学生,经过几个月,哪怕是几个星期的培训即可胜任,不提升自己的技能,后果大家都知道的。
下面分别针对上面的各个目标,为大家带来一些工具和经验的分析。当然这些也只在我的经验范围之内,欢迎大牛前来补充。
提到软件质量目标,首要要明确合格的代码有哪些要求,主要指标如下:
1> 总行数:包括空行在内代码的行数。
2> 函数圈复杂度:圈复杂度是指一个函数可执行路径的数目。以下语句为圈复杂度的值贡献为1,if/else/for/while语句,三元运算符语句,if/for/while判断条件中的||或&&,switch语句,后接break/goto/return/throw/continue语句的case语句,catch/except语句等。
3> 函数深度:函数深度指示函数中分支嵌套的层数。
4> 语句数目:在C++语言中,语句是以分号结尾的。分支语句if,循环语句for/while,跳转语句goto都被计算在内,预处理语句#include/#define/#undef也被计算在内,对其他的预处理语句则不做计算,在#else/#elif/#endif之间的语句将被忽略。
5> 分支语句比例:该值表示分支语句占语句数目的比例,这里的“分支语句”指的是使程序不顺序执行的语句,包括if/else/for/while/switch。
6> 注释比例:该值指示注释行占总行数的比例。
7> 函数数目:函数的数量。
8> 平均每个函数包含的语句数目:总的函数语句数目除以函数的数目。
第一个要介绍的软件为Source Monitor,这是一个代码质量检测的基本工具,即计算上述指标的工具。
对于开发人员来说,可以将其添加到Visual Studio、UltraEdit等等一系列的编辑工具中,具体添加方式可以参考Source Monitor的文档。而测试人员需要了解如何使用命令行模式,建立一个工程,并筛选出不符合要求的目标。
下面是一个参考目标(来自于华为):
1> 圈复杂度7以下;
2> 最大行数1000以下;
3> 每个类最大方法数30以下;
4> 每个方法最大行数50以下;
5> 最大深度5以下。
对于C++来说,检查其代码质量非常困难,尤其含有大量的指针,模板等方面,所以可以看到的代码分析工具并不多,一般大多采用PCLint进行静态代码检查。
PCLint是一个非常严格的编译器,可以发现很多编译器并不提醒的错误,以次来提高代码的质量,例如说识别内存越界问题,未初始化的变量,空指针操作,冗余的代码,执行效率问题等等。
对于开发人员,每次提交代码,都必须执行PCLint,保证修改代码的安全性。对于测试人员,应该配置PCLint执行环境,定期对代码进行扫描检查,为开发人员提出警告。
PCLint的基本目标即无编译Error和Warning。
这里提到的测试为单元测试,即开发人员主要关心的方面。但是需要测试人员进行管理控制。
理论上,凡是圈复杂度大于2的函数,都应该进行相关的单元测试的编写。具体编写的任务一般由该函数的作者进行完成,当然也有很多测试驱动开发的例子,但是这对一般的测试人员要求过高,需要对整体的框架以及代码非常了解,能够明确定义开发框架和函数才能胜任,所以就不过多赘述。
单元测试的工具,目前大多采用GTest、GMock以及MockCpp这几个,mock工具只需选择其一即可。
在这个环节,开发人员每次添加函数,如果圈复杂度较高,需要添加对应的测试函数。而测试人员应该每次构建时进行冒烟测试,保证所有测试函数通过,另外,应该结合圈复杂度的基准,如果圈复杂度较高却没有测试函数的,应该给与警告。
这个方面需要测试人员关心,主要工具可以采用Visual Studio中集成的测试工程,然后录制脚本,通过执行脚本,达到系统测试的目的。
一般测试之前需要测试用例的编写任务,这个任务以前是开发人员进行编写,这样针对代码的修改量,修改范围,用例可以写的非常详细,针对性也非常强。参考基准目标为:测试用例数目/代码修改行数 = 1/3。
测试结束,进行正式版本发布时的标准应为:失败测试用例数目/有效测试用例总条数< 3%。当然,进行测试版本发布时,可以针对不同方面调节这个值的大小。
如果不满足版本发布要求,应该针对Bug修改代码,进行测试用例的追加,直到符合该范围的要求,才能进行版本发布。
由于我不是测试人员,所以对系统测试这方面了解不是很多,包括可以用的工具进行自动化测试等,如果有大牛有经验,欢迎指教。
这一点主要依赖于产品评审会议,组织产品人员、开发人员、测试人员、用户等进行产品需求的评审。这样可以明确需求,同时可以精化需求,降低开发时间,快速进行迭代,推动产品的前进。
由于我对这方面的理解不是很深,欢迎有深刻理解的同学进行补充。
前面针对软件质量的各个方面的目标和达到目标的方法进行了描述,当然没有代码复查的相关部分,这个一般不同的公司组织方式不一,在这里不进行赘述了。但是要把上面这几部分综合起来,进行自动化的控制,每天,或者每次代码提交都进行检查,那么就可以大幅度减少人力,大家也可以抽出时间来进行交流学习,继续提升自己,岂不是很好吗?
既然说到这,达到目标自然也是可能的。首先,需要自动构建管理工具:Cruise Control,这个工具分为.Net版和Java版,当然最好还是选择后者,因为可以进行搭配使用的工具很多。这个工具的主要功能是,提供一个服务器端,用户进行任务的配置,例如说编译,各个目标的检查,最后按照时间或者代码的更新,执行各个任务的检查,将失败的任务告诉用户。
其次,各个任务的执行,可以使用Ant,Ant是基于Java的一个build工具,使用Ant可以指定编译期间不同的任务,然后Cruise Control中指定这些任务,就可以达到了自动化执行的目的。
最后,结果的统计与表示。Cruise Control只是一个管理工具,具体的数据需要测试人员进行收集,并统计对应的部分。例如本月一共提交了多少个用例,有多少执行失败了,等等这样可以表示描述软件质量的信息。为了达到这个目标,一般测试人员都需要掌握一种或多种脚本语言。
软件行业发展了这么多年,大家都追随在“敏捷”之后,我没有从上一个时代走过,不能说这真的是“快了”还是“慢了”,质量到底是“高了”还是“低了”,大家都在拼命的赶时间,赶进度,赶版本。
最近总想写点什么,总结点什么,话题很多,自己却认识很浅薄,常识被那些大牛们践踏的不敢说对还是错,只感觉路越来越长,感谢这些伟人们,开拓了这条不归路,我还有走完的那一天么?
时代在改变,技术在进步,即使没有坐上最后一趟技术潮流的末班车,我们也应该步行走向未来!原文地址:http://blog.csdn.net/feng_ma_niu/article/details/39120447