生活中有这么一种现象:若是你关注某些东西,它就会常常出如今你眼前,例如一个不出名的歌手的名字,一种动物的卡通形象,某个很是专业的术语,等等等等。这种现象也叫作“孕妇效应”。还有相似的一种效应叫作“视网膜效应”,它讲的是:你有什么东西或者特质你就特别容易在别处发现你有的这类东西和特质。干了多年测试的我就会常常发现平常使用的系统中有不少的bug,而我老婆就发现不了。今天要说的事儿是“重现难以重现的bug”,这件事儿在本周共碰见了4次:第一次是微博上有一篇《程序员,你调试过的最难的 Bug 是?》(后面会附上);第二次是一个同事跟我抱怨,好几个bug难以重现特心烦,并问我怎么处理比较好;第三次是本周上线的产品出现了一个当时难以重现的bug,咱们对它作了初步的分析;第四次是翻看史亮写的书《软件测试实战》,偶尔翻了翻,居然看到一小节在写“处理难以处理的缺陷”。这时候,脑子里不少东西聚集到了一块儿,我想仍是记录一下吧。下面是正文:html
也许测试人员(尤为是对新手来讲)在工做过程当中最不肯遇到的一件事情就是:在测试过程当中发现了一个问题,以为是bug,再试的时候又正常了。碰到这样的事情,职业素养和测试人员长期养成的死磕的习性会让她们以为不能放过这个bug,可是重现这样的bug有时候须要花费大量的时间,有的时候还有一些盲目性(由于黑盒测试的缘故,不少内部状态是不可见的,所以没法获取有效的信息来作跟踪),效率较为低下。在实际工做中,时间和进度摆在那里,在经历了屡次痛苦的失败尝试以后,测试人员的处理方法通常会有以下几种:1.向开发人员寻求帮助来重现bug;2.当作一个issue报给开发人员。但是这样的作法存在以下问题:linux
1.开发人员责任心不够强,不肯意花太多精力去求证这件事情,常见的回复就是:在我这儿没事儿啊,我也重现不了,bug关了吧。结果随后在生产系统上,bug又开始sui随机出现了。程序员
2.就跟测试人员不擅长编码和调试同样,开发人员并不擅长找出bug。通过一番尝试之后,他们也找不出什么问题来,常见的回复同第一条是同样的。bug上线后又出现的宿命也是同样的。数据库
这时候,真正的问题来了:如何捕捉难以重现的bug?这件事儿对于测试人员来讲就这么难么?windows
答案并不那么乐观,重现“难以”重现的bug原本就是一件“难以”完成的事情。但“难以”并非不可能,经过一系列的测试、分析方法,咱们是可以抽丝剥茧把绝大部分隐藏的很深的bug揪出来的,固然有的时候你要考虑投入产出比,但投入产出比不是本篇要考虑的,本篇只讲一些我积累的经验。缓存
为何不能重现bug?安全
最大的缘由就是:测试人员对被测物的了解还不够深刻。服务器
我曾经作过一段很长时间的收集和统计,那些被称做过“难以重现”的bug最后均可以分为以下几类:网络
1.环境的变动形成了bug难以重现,这里的环境包括了:基础软硬件环境(操做系统、网络、存储、中间件、容器等等),被测物自身发生了某些变动。环境的变动通常是因为多人共用环境形成的,也有少许状况下是系统内部或者时间触发的变动(这类bug很是难重现)。多线程
2.没有找到真正引起bug的操做。这些操做每每是一些不怎么显而易见的操做,测试人员在不经意间完成,而又忽略了这一操做,以至难于重现bug。
3.没有找到真正会引起bug的操做序列。不少bug的重现须要知足多个条件。在知足多个条件的状态下,你作了对应的操做,bug才会被触发。
4.bug必须使用特殊的数据才会出现,测试人员没有意识到她使用的数据的特殊性。一种比较难搞的状况是:同一组输入,在不一样状况下(不是不一样的业务状况)输入会被转化成不一样的数据。我曾经见到过这么个例子,程序员用系统当前时间做为随机数的种子来生成id,可是id设置的比较短,一个存储的操做使用这个id,在一些状况下,发生了冲突,当时作功能测试这种小几率事件耗费了测试人员大量时间也没有稳定重现,仍是在性能测试的阶段测试了出来。
5.测试人员因为错误操做,出现了误报(这很常见)。好比,记得本身执行了step3,其实没有,或者没有正确执行step却以为正确执行了。
怎样对付这样的bug呢?
我喜欢James Bach 说的那句话:测试就像CSI。CSI是Criminal Scene Investigation 的缩写,也是我很是喜欢的美国系列剧。
从我来看CSI的精髓在于:仔细观察,详细记录,科学分析,严密推理,有序求证,大胆假设,持续不懈,团队协做和一点儿运气。找bug其实和CSI探员作犯罪现场调查没什么太大区别。得知道,你工做的重要性一点儿不亚于CSI探员。
仔细观察:第一件事情就是要观察,观察你所作的一切操做和一切相关的系统反馈。在一开始,观察的重要性要远远大于思考,经过观察你得到蛛丝马迹,这些蛛丝马迹是你进行思考和假设的关键输入。例如,我在一次测试的过程当中,发现作某种操做的时候会至关慢,极少数状况下还报错过一两次,当询问了开发人员后得知这个操做的后台实现步骤是:先查看数据是否在缓存中,若是不在,则从远端服务器请求数据。我抓住少数状况下会报错的这一现象,仔细观察它的出错信息后猜想报错并非由于网络链接不稳定引发的,而是因为远端服务接口实现有问题引发的,后来从新设计了测试用例,果真找到了问题所在。若是不仔细观察出错信息,就会听信开发人员认为这是网络不稳定引起的正常issue而错过这个bug。
详细记录:详细记录你的操做步骤及返回结果很是有助于回朔问题,也有助于后续分析。准备一个word文档,和截图工具备时候很是必要。另外,在观察的时候,你不只要注意被测物的最终返回,还须要观察过程当中的一些中间状态,每每这些中间状态提供的信息才是解开问题的关键。这些中间状态通常会被记录在log文件中,所以知道你的被测物是如何记log的,log被记录在哪里很是重要。log给了你另一个看系统的角度。log是有级别的,若是级别能够动态调整,在找比较难找的bug时,能够将log记录的级别调至最低(DEBUG级)让它们记录更多内容。利用系统的错误转储文件(好比linux的core dump文件,windows下也有相应的记录转储文件的方式)分析也是个不错的办法(须要较高技术能力),但通常建议测试人员把这些转储文件交给更专业的开发人员来分析。在我短暂的C++开发岁月中,有使用过GDB阅读转储文件的经历,那绝对不是愉快的回忆。你瞧,测试人员的主要工做是找出可重现的bug,并非定位它们,不是么?
除了log,若是能有监控信息,也要查看他们。好比系统提供的监控平台,监控日志;应用本身的监控平台、监控日志(若是有的话);采用一些外部技术手段截取一些中间状态信息,如使用sniffer抓取通信包,使用Fiddler截获HTTP报文内容;给运行程序插桩来查看内存,堆栈,线程,函数被调用状况等状况,如Jprofile,gpertool等等。
科学分析:对于黑盒测试人员来讲,科学分析意味着你须要有必定的分析策略。咱们须要采起一些形式化的方法来完成咱们的分析。基于个人统计,缺陷难以重现有很大一部分缘由是由于“没有找到真正引起bug的操做序列“。测试人员不可能像开发人员那样去跟入到代码内部,设置断点调试程序,他们主要的测试方式是直接来操纵被测物,并从外部观察被测物状态的改变。显而易见,“状态转换图分析法”是测试人员对付“难以重现bug”的最强有力武器之一。状态转化图可以帮助测试人员很好的选择操做路径,而且知道这么作有什么意义。“状态图转化法”绝对是测试人员值得花时间学习和研究的一种方法,你能够走的很深,也能够研究得很远(能够从MBT的方向进行拓展),限于篇幅,这里就不展开了。在这里推荐《探索吧!深刻理解探索式软件测试》这本书,它的第八章对“状态转换”作了很是实用的描述。
上文分析的让bug难于重现的另外一种缘由是没有找到“真正引起bug的特殊数据”。个人经常使用作法是这样的:1.画出系统交互图(要真正弄清系统的边界,这很重要),并识别出每种交互会有什么样的输入、输出数据和中间数据,识别出这些数据的规约和格式,这样你就不会对数据有遗漏。2.考虑数据的等价类、边界值,对这些输入进行组合,分析数据之间是否有耦合关系,若是有耦合关系,弄明白关系是什么,设计违背这些关系的用例,最后采用组合测试工具初步生成测试集,再人工选取最可能出问题的数据集(直觉有时候很是管用)。
严密推理:天马行空对测试人员很重要,可是当你试图重现一个bug的时候,这并非一个很是好的方法。抓住了蛛丝马迹,你就要推理是为何产生了这种蛛丝马迹。限于工做性质,测试人员更多的会从:业务完整性、数据完整性、业务正确性、数据正确性等方面考虑问题。可是,若是测试人员对被测物的IT架构有比较深刻了解的话,推理的范围会扩大到技术实现层面,如:多线程可能引起的问题,网络引起的问题,excepiton处理不当引起的问题,全局事务设计不当引起的问题,内存泄漏引起的问题,数据库表设计不合规引起的问题等等等等,这些会让你的分析推理能力如虎添翼。固然,若是限于条件,测试人员不具有这类能力,则应该在适当的时候请求开发人员协助。
有序求证:这里只有一点须要注意。那就是,在求证的过程当中不要打散弹枪,按照你的推理一步一步的来,一个个推理的来验证,一次只引入一处修改。这样才能让你的捕虫网编制的足够细密。
大胆假设:有的时候,看似八竿子打不着的东西居然存在着千丝万缕的联系,而你获取信息的过程老是一个由少及多的过程,一开始这些联系是没法被识别出来的。经过推理,逐步验证,你慢慢的识别出了大部份内在联系。但有时候这种逐步推动的工做也会有局限性,工做若是出现了瓶颈(你试遍了你全部的假设,都没有重现bug),这时候就须要发挥一点儿想象力了,天马行空这时候从必定程度上又变得有用起来,固然天马行空也不是无厘头,还得靠咱们所谓的“灵光一闪”,这号称是潜意识在帮助你。CSI的剧情中不也老是出现这种柳暗花明的桥段么?
坚持不懈:话很少说,有的时候你差的就是那么一点儿点儿耐心。
团队协做:不少状况下,重现bug不是一我的能搞定的。咱们须要测试环境ready,测试数据ready,各类监控、分析工具ready,各类不一样的视角开拓思路、加深对被测试物的认识。这是team work!!!独行侠有时候很管用,可是终究有极限。这就是为何CSI是一票人在作而不是一两我的在作。
一点儿运气:说实在的,有的时候重现bug就是靠运气,你不得不认可这一点。事实上不少美好的事情发生都得依靠运气,好比中彩票。要记住的一点是,运气是创建在你不懈努力的基础上的。若是你一张彩票不买,你确定什么也中不了。但若是你坚持买上几年,中个五块十块甚至二百也不是梦。
Let it go:全试过了,连运气都没有。你只能放手,回到最上面我说的那两条了:找开发来帮忙,或者给开发报issue。btw,即便不能重现bug,也应该给开发人员提供更多信息:如log、dump文件、监控记录、屏幕截图等。作一个负责人的测试人员,把烦恼真实的留给下家,这很重要:)
最后给出一个软件调试大牛 David.A.Agans对于软件调试的九条建议,很是值得一读:
http://sydney.edu.au/engineering/aeromech/MTRX2700/Course%20Material/lectures/PDF/week04/Debugging.pdf
9月25日:今天学到了一个词:Heisenbug ,这词的中文意思能够被翻译为“神出鬼没的bug”。。。这个单词和量子力学元勋海森堡的名字差了一个字(Heisenburg) 量子力学的一个经典定理就是"测不许原理"。 你们的吐槽能力真强。 想了解细节请看下面的这篇文章:
http://blog.csdn.net/kjfcpua/article/details/8125306
9月26日:以为有一些实例会更容易理解,我会尽可能收集一些例子放到这个帖子里:
12月3日更新: 一个Ajax同步异步引起的血案 一个hotfix的debug过程:)
12月3日:其实上面的大段论述是站在测试人员角度上来看的。我也写不少代码,做为一个开发人员(哈哈)我查错的方式通常是:
1.先能稳定的复现错误。(这一步最难)
2.设桩,打印出一些中间状态来分析,看到那一起错了。
3.摘除不相干的代码,慢慢迭代,定位错误。
4.若是发现调用第三方依赖跟你想的不同,先读文档,而后再测试第三方依赖。有问题再想办法。
5.良好的程序设计和单元测试覆盖度才是不二法门,会让你的调试变得简化的太多。。。用过都说好:)
6.测试工做的确加强了我排错的能力。coding的同窗都尝试作几个月测试吧。会有至关大的好处。
14年12月15日:早上的一个同事提出了一个小技巧,以为很不错,记录一下:若是你没有办法稳定的复现bug,能够经过几率统计的方式将问题反馈。若是10次里出现一次问题,不足以打动开发人员重视问题,能够经过自动化测试的方式提升执行次数的数量级,若是一千次出现50次~100次。这个问题就足够引发重视了。在negotiate的时候,这是一种好思路。
----------分割线,下面是转载的文章--------------------------------