转载之:http://www.uml.org.cn/Test/200911195.asp
Bug之一——bug的前世今生
Bug这个名词已经很老了,从网络上查到的资料:
>>>>>>
1946年,Grace Hopper在发生故障的MarkⅡ计算机的继电器触点里,找到了一只被夹扁的小飞蛾,正是这只小虫子“卡”住了机器的运行。Hopper顺手将飞蛾夹 在工作笔记里,并诙谐地把程序故障称为“bug”。bug的意思是“臭虫”,而这一奇怪的称呼,后来演变成计算机行业的专业术语。虽然现代电脑再也不可能 夹扁任何飞蛾,大家还是习惯地把排除程序故障叫做Debug(除虫)。
<<<<<<
关于Grace Hopper其人,参见http://en.wikipedia.org/wiki/Grace_Hopper,上面有这位美女强人的照片和史上第一只(个)bug的照片以及生平介绍。
关于bug最出名的事件就是“千年虫问题”了,关于损失最惨重的bug,目前有导致28位“自己人”被炸死的爱国者导弹事件,还有其他诸如火箭发射失败等等,这些是目前为止有关bug最惨痛的记忆了,至于以后会不会有更惨重的,可能吧。计算机是万能的,因此计算机里面的bug也是万能的,相信我吧。不信的话你可以看看《战争游戏》,总共两部,看看之后你就会知道计算机在某种情况下是可以毁灭人类的,当你看完之后如果惊出一身冷汗,记得提醒自己幸好是电影~~
最近关于bug的消息也不少,IE有个bug不是藏了好多年最近被人给挖出来了么。下面是添油加醋过的报道截屏:
看着那个“全球新的灾难”,是不是觉得很遥远啊。
看吧,bug多厉害啊,作为bug猎手的测试人员,是不是觉得特有自豪感,觉得自己的职业特伟大~~ 先别太激动,因为下一篇我将要结合经验讲一些关于什么是bug话题。
Bug之二——“庐山真面目”
在介绍bug的光辉历史的时候,不经意间给bug蒙上了一层神秘的面纱,仿佛bug是一个遥远的神秘的事物,如同金字塔之中的种种。其实bug本身并没有那么神秘。
首先给出一个“官方”的定义,来自Ron Patton《软件测试》:
>>>>>>
至少满足下列五个规则之一才称发生了一个软件缺陷(Software bug):
1) 软件未实现产品说明书要求的功能
2) 软件中出现了产品说明书中指明不应该出现的错误
3) 软件实现了产品说明书未提到的功能
4) 软件未实现产品说明书虽未明确提及但应该实现的目标
5) 软件难以理解、不易使用、运行缓慢或者——从测试人员的角度看——最终用户会认为不好
<<<<<<
关于上面的“官方”解释我就不做赘言了,“照搬”是没有意义的,所以如果需要了解更多,请阅读原书《软件测试》(Ron Patton)著。从我自己的经验来看,所谓bug,大致可以分为“该做的没做,做了不该做的,没有按要求做”,我自己就是这样简单的来对bug做出分类。
所谓“该做的没做”的又包含了三个部分,即
对于上面的“该做的没做”三种情况应该区别对待,对于第一种就很直接了,产品说明书中列出来的功能没有实现,直接作为bug提交了;第二种稍微麻烦一点,需要跟开发人员统一认识;第三种就更加麻烦了,这时候需要开发人员项目经理测试人员一起合计合计了,统一认识之后再决定怎么处置这个bug。
所谓“做了不该做的”,这句话是作为测试新人和开发人员来讲最难理解的bug,这里并不是指它的概念或者意义等理性可以解决的事情,而是感性层面的“理解”。试想开发人员好不容易做了一个新功能,测试人员却拿着“产品说明书”这根鸡毛当令箭来要求删除,大多数人可能会想到,额外的功能总是好的……但是,需要提醒的是,每一个额外的功能都有引入额外的bug的风险,这不仅仅增加了测试的工作量也增加了产品的质量风险,更重要的是,增加的额外功能的合理性是否确定。比如我们开发一个课程管理系统,一般情况下产品说明书中要求给普通用户的功能只有登陆和浏览,这个时候如果开发人员提供了其他额外的功能如搜索,排序甚至编辑,这就会出现问题了,这些额外的额操作会额外增加系统或者服务器端的负担,甚至具有安全问题。当然,对于“做了不该做的”类型的问题,需要找到相关开发人员与PM一起确定,测试人员是不应该自以为是地作为bug提交,至于原因,在后续的文章中会提到。
所谓“没有按要求做”,这里的要求是指“应该做成什么样子”,这些内容是依据产品说明书来定的,比如一个“职位”选项,产品说明书中明确指出使用“下拉框”来处理,而开发人员偏偏使用了“文本框”,这些都是bug。对于这种类型的bug,辨别和处理相对就比较简单了,而且我们找到的大部分bug也都是这种类型。
关于bug的定义,其实直到现在都没有“官方定义”,这也为什么我引用Ron Patton的话的时候使用的是“‘官方’定义”的原因了。上面提到bug的时候,总是带着一个亲属“产品说明书”,实际上在当前中国很多软件项目中并没有这个东西,因此在项目实践过程中,上面提到的内容千万不要生搬硬套,至于怎样灵活处理,在后续文章中也将提到个人见解。
Bug之三——“自定义”bug
所谓“自定义”是指针对于当前很多软件项目中没有“产品说明书”或者没有合格的“产品说明书”这类现实主义情形而言的。
在上一篇文章中介绍了bug的“官方”定义和个人见解,其中涉及到了“产品说明书”这个对于当前一些软件项目而言尚属理想主义产物的东西,在这篇文章中,我分享给大家的就是我之前所经历的一个类似的项目经历,当然也要献给大家一些我的“偏方”。
对于同一个问题,不同的人会有不同的看法,“一千个观众眼中有一千个哈姆莱特”就是这个意思。这句话用来形容开发人员和测试人员对于bug的态度就更加贴切了。很多的时候,当测试人员发现并提交了一个bug之后,得到的开发人员的回应是“This is not a bug”(这不是一个bug)或者“这不是一个严重的bug”之类,我第一次听到这句话的时候心情是相当沮丧,进而转化为愤怒。
没有“产品说明书”,凭什么这是bug?这的确是个问题。问题生来就是被解决的,所以我们一定治得了它。最直接最天真的想法可能就是建立起一套完善的机制,有了产品说明书不久结了么。果真是个好办法,可是太理想主义了,这就如同老鼠挂铃铛故事中的那一群老鼠一样,办法是很好,但是却无法实施或者很难实施。一套合理的软件流程不是朝夕之间的事情,更何况一套完善的机制也不可能完全杜绝未定义bug。怎么办呢?另一种办法来了:有事好商量呗。
在进行讨论之前,测试人员还是有些准备工作要做的。对于“bug”这种罪大恶极的东东,我们应该列举其罪证。这个时候可能拿着《软件测试》上面的东西来解释就显得有些苍白无力,这毕竟是测试人员的“圣经”却不是开发人员的。夸张点就像两种信奉不同宗教的人一样,可以说是一个信奉上帝,一个信奉阿拉,你叫人家阿拉伯人跟着美国人信奉基督教,那显得有点过分了吧。更何况“圣经”上面讲的东西也是泛泛而谈,并没有严格指出bug到底长什么样子。这个时候更加合理的办法是列出自己的考虑的点,也有更有说服力的做法——找一些可以印证自己看法的知名软件产品中相同或者相似功能点,在这一方面“微软”可帮了不少“忙”。
开始讨论了,双方本着实事求是的态度来讨论这是不是一个bug,这就如同一场法庭辩论。测试人员作为原告代理律师,开发人员作为被告代理律师,项目经理作为法官。记住,我们只是律师来的,所以宣判的结果并不会直接影响到我们,因此测试人员千万不要把自己当成原告,开发人员也不要把自己当成被告他爸(实际情况却经常这样,测试人员把bug当作自己的战利品,而那可是开发人员辛辛苦苦创造出来的软件啊),我们应该以理性的态度来审视这个待定的“bug”。开庭之后,原告代理律师陈述己方观点,接着被告代理律师陈述,然后双方进行短暂的和平而友好的辩论,然后宣判,不准上诉。
当然还需要注意的是,这个会议尽量不要占用大家太多的时间,也没有必要十分正式的邀请一大帮人来开会,但是开会完毕之后,测试开发双方要无条件接受讨论结果并及时进行相关的处理。
笔者经历的项目中,很多时候都是测试人员取得了最终的胜利,这倒不是因为笔者强势或者咄咄逼人,更多的时候我拿出相关著名软件的例子之后并陈述为什么应该这样做之后,开发人员便接受了这样的解释。当然,有时候也是测试人员不了解软件产品相关领域知识而导致了误报bug,这个时侯测试人员应该心悦诚服地接受“This is not a bug”,这个时候任何多余的动作或者言语都是无效的,不仅不会给自己带来什么好处,也会给项目组带来不和谐的因素。在全民建设社会主义和谐社会的年代,大家都要和谐~
Bug之四——好心办坏事?
“做了不该做的”bug,到底是是测试人员“狗咬吕洞宾不识好人心”还是开发人员真的“好心办了坏事”?用当今最流行的话来说,这的确是一个“纠结”的问题。
这两种情形在实际的测试活动中都会存在,有些时候的确是测试人员急功近利或者太死板,险些扼杀了一个很好的idea;有些时候确实又是开发人员因为这种或者那种原因,别出心裁却导致了画蛇添足。
还是前面的观点,问题生来就是被解决的,只是这个问题稍微显得麻烦了一点而已,处理起来比较棘手,稍有不慎就会得罪开发人员或者测试人员中的某一方。这个问题和上一篇文章中提到的“自定义”bug其实是一个问题,即开发人员与测试人员的关系的问题。
测试理论中经常出现的其他学科术语莫过于“经济学”“心理学”(如在测试结束标准中考虑到的“测试经济学”),“心理学”范畴主要讨论的就是测试人员与开发人员之间的关系处理问题。一个比较流行的说法称开发人员是创造者,而测试人员毁灭者,肆无忌惮的破坏者,笔者是很赞同这种说法和做法的,但是仅限于在测试技术本身,而不包括测试整个过程。作为人来讲,人都有惰性,人都有好胜心,人都有各种各样正常的不正常的自尊心,这才是问题的症结所在。
测试人员A发现了一个“做了不该做的”bug,这时候多多少少有了一点点炫耀的虚荣心,一点点自豪感,然后这个时候他兴冲冲找到相关开发人员B告诉他“你这个功能是画蛇添足的”,这个时候引来的是开发人员的反抗,这是因为一个bug意味着他要做额外的工作了(与人的惰性相斥),另外被人说自己写出来的代码有问题会不经意间伤及他的自尊心,然后B开始为自己辩解,双方互不相让(这是好胜心在作祟,当然也包括自尊心等等因素),事情开始变得糟糕了。
解决办法其实说起来很简单,引用某部影片(《十全九美》)里面的那句台词——“淡定”。大家都应该淡定,在遇到说不清的bug,在双方都认为自己对的时候,要保持淡定。问题需要理性的解决,而不是因为感性的因素而互不相让,这样对于问题的解决没有任何帮助。笔者不提倡有些哲人提出的“别人打了你一耳光,那把另外一面连伸过去让他打”这种“高风亮节”,笔者推崇的是理性解决问题。对方能否保持理性的态度来解决问题那是对方的问题,作为我们测试人员自己,要保持淡定。找到了bug,“胜不骄”温和提醒开发人员并提出自己的看法或者推荐的解决方案;当遇到被打回的“bug”,“败不馁”镇定分析被打回的原因,如果必要可以“Reactive”并再次向相关人员解释自己的看法。遇到温和的同事,要淡定,不能一副泼妇的模样去“欺压”良民,欺负老实人不算真本事;遇到脾气急躁的同事,要淡定,耐心说,实在摆不平,找上级,“官大一级压死人”,这是下下策,因为常用这一招会让人觉得你这人总爱“打小报告”,会被同事疏远的。
专门写这些东西出来,本身与测试技术没多大干系,但是却与测试工作的能否顺利息息相关。笔者曾经带过一个新人,因为类似的原因导致了其离开,因此一直深感遗憾,这才想起把自己的一套“馊主意”拿来分享,希望对遇到类似情况的同行有所帮助。
Bug之五——Bug Report
在前面的四篇文章泛泛而谈bug相关的一些问题,只能迟到这篇文章中开始介绍bug本身相关比较实用的知识。先从bug报告开始,Bug报告的洋名似乎比中文名来的顺耳——Bug Report。
一般意义上的bug Report即是将所发现的bug整理归纳起来,随着缺陷管理工具的盛行,这种报告类型的报告已经渐渐失去了意义,但是笔者还是要从这种类型的bug report谈起——一开始,我们先来了解一下单个bug的组成要素。先来看一个bug的简单示例:
Bug ID |
Bug 783 |
标题 |
编辑用户信息功能 “用户职位”字段未保存 |
状态 |
Active |
原因 |
New |
严重级别 |
2 |
描述 |
[Test Cases] AAA_UserProfile_0003 [Precondition] <在此填写预置条件> [Descrīption] <在此填写详细描述信息> [Expected] <在此填写期望结果> [Actual] <在此填写实际结果> |
上表中并不是一个完整的bug的实例,但是基本上已经囊括了bug的基本要素。对于这些基本要素,接下来作简要说明:
1. Bug ID是bug的唯一标识符,类似于公民身份证号码,每个bug有且仅有唯一的一个ID序号。如: 示例中的“Bug 783”
2. 标题 是对bug的一个简要概括。如: 示例中 编辑用户信息功能 “用户职位”字段未保存
3. 状态 是指bug当前所处的状态。如:示例中 Active是指bug现在是活动的,即没有尚未被开发人员修复。bug的状态一般分为三种,即Active, Resolved, Closed,分别对应于活动的(开发人员未解决掉的),被解决的(开发人员声明已解决但尚未通过回归测试验证的),关闭的(已经被开发人员修复并通过回归测试的)。关于bug的三种状态的转换将在后续文章中专门讨论。
4. 原因 是指将bug调整到某个状态的原因。如:示例中的New是指测试者新发现了一个bug,所以把状态调整为Active。Bug状态调整可能有多种原因,即使是对于同一个状态在不同的情况下也会有不同的原因。关于这部分的讨论也将在后续文章中提出。
5. 指派给 是指将bug指派给对应的负责人进行处理。如:实例中的 Q Chen是指项目相关人员处理bug之后将bug提供给下一步处理人员Q Chen。对于指派给的具体人选,不同的bug状态下有不同的要求:测试人员测试发现的bug指派给开发相关模块的开发人员或者开发组负责bug处理的人员,开发组处理后的bug会指派给发现bug的测试人员。
6. 优先级 目前在测试组实际工作中使用时同时包含有严重级别的意义,严重级别是指bug对应用程序或者操作系统的影响程度,严重界别越高的bug等待解决的优先级越高。如: 实例中的 1是指优先级(严重级别)最高的一类bug,这类bug需要尽快解决掉。关于Bug的优先级划分将在本系列文章中的后续篇章专门讨论。
7. 描述 部分是bug提交中最重要的部分,是对bug信息的一个详细的描述。如: 示例中 [Test Cases] APS_SheetJop_0003 ……我们可以发现“描述”被划分为几个子模块,以详细且清晰地描述bug相关的信息,以便bug相关人员处理bug和其他人员了解bug的各个阶段的各类信息。在下一篇文章中我们将详细讨论“描述”中的内容。
Bug之六——斑斑“罪证”
在上一篇文章中给出bug的一个示例,简单介绍了bug自身的一些常用属性,如Bug ID、状态、原因、指派给等。按照约定,这篇文章将介绍bug重现的问题,亦即bug示例中的“描述”部分。
细心的读者会发现,在上面的bug“描述”一栏中又增加了几个用方括号标记起来的标记,这是笔者在管理的过程中添加进去的几个标签。一个完整的bug描述如下:
描述 |
[Test Cases] <出现该bug的测试用例的ID> [Precondition] <在此填写预置条件> [Descrīption] <在此填写详细描述信息> [Expected] <在此填写期望结果> [Actual] <在此填写实际结果> [Found in Build] <发现bug的源代码build号> [Reason] <bug产生的原因,开发人员填写> [Solution] <解决bug的方法> [Resolved in Build] <解决bug的源代码Build号,开发人员填写> [Reopen Reason] <可选,bug被重新激活的原因> |
关于描述的信息在上面的表格上面基本上已经列出来了,因此不必要再一一详述。笔者在实践中即是按照该模式来管理bug的,只要“描述”得当,我们完全可以仅依照这部分的内容就把bug重现出来。当然,有些地方还是有必要解释一下:
1 [Test Cases][Precondition][Descrīption][Expected][Actual][Found in Build]这些标签是由测试人员(严格意义上是bug提交人员,如果可能的话可以要求所有的bug提交者都遵循这种模式以便于管理)添加并填写的,而[Reason][Solution][Resolved in Build]这些标签是由开发人员(bug的修正人员)填写的。
2 有关测试环境的内容不列入单个bug的report中,对于其他仅在少数用例中使用的环境则在预置条件即“[Precondition]”中加以描述。
3 [Reopen Reason]是指测试人员发现不过没有被修改但是已经被开发人员标记为修改的bug或者已经被关闭的bug死灰复燃,这个时候测试人员需要重新激活该bug,因此除了需要注明重新打开的原因之外,一般还需要在其中添加如诸如Build版本信息等内容。
4 当一个bug被重新激活之后,一个bug进入一个新的生病周期,这在bug的描述中体现为[Reopen Reason]标签之后会跟着出现[Reason][Solution][Resolved in Build]等有开发人员填写的标签以描述bug的修正情况。
5 [Resolved in Build]中的内容一般形式上写为版本号加上一个“+”表示该版本及以后的版本中该bug都不会被重现了。如SystemA1.0.1320+
Bug之七——优先级
Bug的优先级是bug管理过程中必须考虑的问题。对于优先级的划分,不同的软件公司有自身的一套制度,因此笔者介绍的也仅仅是自己比较喜欢的一种方式。
为了便于bug的提交和管理,也为了方便于与开发人员进行交流,笔者倾向于在项目中将bug划分为三个等级,而不是网络上流传的五等级版本甚至七等级版本(在成熟的软件组织中,使用更细的bug等级划分有利于bug分类,质量评审等工作的顺利进行)。这三个优先级分别即优先级1(严重的),优先级2(比较严重的)和优先级3(一般的)。
笔者划分bug等级的指导思想很简单,严重影响测试执行的bug是最严重的,即优先级1 的bug,除此之外所有导致应用程序崩溃掉的bug也列入到优先级1中;其他功能性bug列入比较严重的bug的队伍,即优先级2;界面上的bug列为一般的,即优先级3。
作者在实践过程中推行的就是这种bug分级制度。这种分级制度看起来过于主观,而不像网络上流传的各个bug优先级划分的版本中,将每个优先级的bug的表现都一五一十列出来,如下是笔者以前使用到一个bug优先级划分文档中列出的优先级1的bug特征:
a) 应用程序某个模块功能未实现(包括整个模块不能运行)
b) 用户的信息被破坏或者丢失
c) 可重现的不可避免的崩溃,死锁
d) 功能和性能急剧衰退
e) 严重的内存泄漏
f) 导致功能无法正常使用的UI设计(UI响应迟缓)
g) 其他
的确,这些bug优先级划分很明确,让人一目了然并且觉得很有道理,可是拿到实际中一用,麻烦开始来了。因为某些描述仍然不够详细,含混不清的描述诸如“功能和性能急剧衰退”,碰到这种描述,不同的人会有不同的理解,而不同的理解必然会带来各种各样的问题。因此,笔者在实践中逐渐摒弃了这种做法(当然,笔者并不排除将来可能还是会回到一些比较正规的管理方法上来,但是目前这种“标准”方法并不适合笔者所在的公司~),并开始逐步推广笔者自己刚才提到的粗放式bug优先级划分方法。
对于该划分方法,笔者还需要进一步的说明。笔者刚才提到的“严重影响测试执行的bug”其实也是指系统的基本功能或者核心功能,比如新建编辑删除功能中,对于同样是信息为保存到数据库——即新建后记录未添加到数据库,编辑后记录未更新,删除后数据仍然存在于数据库中——这时候笔者仅仅将新建功能的该bug置于优先级1中,编辑删除bug则置于优先级2中。这种方法与很多正统的方法很不一致,因为在很多划分方法中“信息未保存”都是优先级1的bug。但是笔者自认为这样做是有理由的:当新建功能发生该类型bug而编辑删除功能正常时,编辑删除功能仍然无法测试或者实现(因为没有数据啊),这在客户的江渡看来会直接视为新建编辑删除功能均未实现。新建功能正常而编辑或者删除功能失效,则不会影响到其他功能的使用(当仅编辑功能失效的时候,新建和删除功能并不会受到影响),测试人员仍然进行新建删除功能的功能测试,客户依然可以使用新建和删除功能。
当然,笔者使用上面的划分方式还有其他的原因——基于bug管理和测试开发工作的顺利推进。读者可能会注意到,使用上面的bug划分方式会减少优先级1的bug的数量,笔者这样做是因为笔者在bug管理中推介的方式是优先级1的bug不允许推迟到下一个工作日修改。试想,如果优先级1的bug的数量如果过多自然会导致这种管理方式推行的极大阻力——没有哪个开发人员会喜欢让自己一整天的时间花费在修复bug上。当我们提交的优先级1的bug都是非常紧急的,会影响到开发或者测试的进度的话,开发人员就自知理亏不得不去修复这些bug了,这就保证了即使到了项目很急迫的时间,我们项目的主体功能还是稳定可用的,并有效遏制了严重bug的生存期。
对于优先级2与优先级3 的划分点,只是笔者个人看法,因为笔者目前所经历的项目都是功能性为主,因此对于UI相关的要求相对较低,因此笔者采取了这种粗放的方式(将UI相关bug归纳为优先级3,其他的非UI的非优先级1的bug全部塞到优先级2 的集装箱中~)。
Bug之八——bug的生命周期
Bug的生命周期是bug管理中另外一个需要事先规范的管理点。
所谓的bug的生命周期,笔者将其简单的理解为bug状态在什么时候怎样转换,基于什么原因转换。一般我们将bug的状态划分为三种,即活动的,已解决的,已关闭的。活动的即尚未被修复(处理)的bug,已解决的即开发人员对bug进行了处理(包括修复,或者标记为延期处理等)但是尚未得到测试人员对处理进行验证的bug。
在介绍bug生命周期的时候,大多数作者都习惯于拿出一副生命周期转换图,笔者亦不能免“俗”~
上图是一个简化了的bug生命周期管理,覆盖了日常工作中常见的bug的管理机制,关于bug的生命周期的管理,还有一些注意事项: