测试主要是发现错误,调试(也称纠错)则是肯定错误的缘由和准确位置,并加以纠正。 程序员
调试是包含2个步骤,从执行了一个成功的测试用例、发现了一个问题以后开始。第1步,肯定程序中可疑错误的准确性质和位置;第2步,修改错误。错误定位是一项技术活,是有必定难度的。目前有4种常见方法。 小程序
调试程序的最为广泛的模式是所谓的“暴力”方法。这种方法之因此流行,主要有2个缘由: 工具
1)它不须要过多的思考,是耗费脑力最少的方法。新手上路,都喜欢这种方法。 测试
2)人遇到bug,可能会自尊心受损、热情耗尽、也可能迷失方向,(总之心情很差,须要发泄啊),因此使用暴力虐待程序。 .net
暴力调试方法,主要有3种类型: debug
(1)利用内存信息输出来调试 设计
使计算机将存储器的所有内容和地址打印出来,而后在这数以万计的数据中寻找错误所在。这种方法也叫主存信息转储,虽然有时能得到成功,但更多的是白白浪费时间、纸张和人力。 调试
(2)在程序中各点设置打印语句 对象
本人,大学时代经常使用这个方法,一个print,输出变量值。。。 ip
(3)使用自动化的调试工具进行调试
自动化调试工具的工做机制,相似于在程序中插入打印语句,可是不修改代码。
还有一个效率改进型的暴力法。
(4)2分查找(对分查找法)
对分查找法。这种方法主要用来缩小错误的范围。若是已经知道程序中的变量在若干位置的正确取值,能够在这些位置上给这些变量以正确值,运行程序观察输出结果,若是没有发现问题,则说明从赋予变量一个正确值开始到输出结果之间的程序没有出错,问题可能在除此以外的程序中,不然错误就在所考察的这部分程序中。对含有错误的程序段再使用这种方法,直到把故障范围缩小到比较容易诊断为止。
我的经验,暴力调试方法有很多缺点:无关数据量很大;有时须要修改代码(可能忘记删除调试代码);效率底下……
这些暴力调试方法的主要问题在于:它们都忽略了思考的过程。
咱们能够在调试程序和侦破谋杀案之间找出类似点来。实际上,在几乎全部的谋杀悬念小说中,谜案都是经过仔细分析线索,将表面上不重要的细节全联结起来而最终侦破的。这不是一个使用蛮力的方法,要使用蛮力的是寻觅障碍物或搜寻财宝。
还有一些证据代表,不管调试小组成员是富有经验的程序员仍是学生,肯动脑筋而不是依赖别人帮助的人可以更快、更准确地发现程序的错误。所以,咱们建议仅在下列状况下使用暴力调试方法:
(1)其余的方法都失败了(能够结合“2分查找”来逐步缩小出错范围)
(2)做为思考过程的补充(辅助方法),而不是替代方法
经过思考列出可能的缘由,逐个排除,最后找出问题所在。这种方法又分为概括法和演绎法。
概括的思考过程就是从特殊到通常,从线索(即错误的症状,多是一个或多个测试用例的结果)出发,寻找线索之间的联系、归纳出共同特色、得出通常规律。概括的过程以下图所示。
它分为以下4个主要步骤。
(1)肯定相关的数据
调试人员犯的一个主要错误是未能将全部可用的数据或症状都考虑进去。第一步是列举出全部知道的程序执行正确和不正确之处,这些不正确之处便是症状,让咱们相信确实存在错误。那些类似而不相同、且未引发症状出现的测试用例提供了额外的有价值的线索。
(2)组织相关数据
概括意味着从特殊到通常,所以第2步是组织这些相关数据,以便观察线索间的模式。
最重要的是搜寻矛盾。
(3)做出假设(注1)
研究线索之间的联系,利用线索的结构中可能的模式做出一个或多个关于错误缘由的假设。若是没法做出假设,就须要更多的数据,这些数据能够经过设置和执行附加的测试用例来获得。若是有多个假设存在,首先选择最有可能的一个。
注1)所谓假设,就是对于怀疑所产生的问题,提出一种推测性的或可能性的说明。
(4)证实假设
此时,常犯的主要错误是跳过这一步而进入结论阶段,并企图肯定错误所在,这是纠错中常常发生的问题。可是在修改错误以前,证实这些假设的合理性是极其重要的,忽视这一点将致使只能肯定问题的一个征兆或一部分征兆。假设的证实是靠比较它与最初的线索或数据,确信这些假设能够彻底解释这些线索的存在。若是没法解释,要么这些假设是无效的或不完整的,要么还有更多的错误存在。
演绎的过程是从一些广泛的理论或前提出发,使用排除和精炼的过程,达到一个结论(错误的位置)。过程以下图所示。
举个谋杀犯的例子,与概括过程相反,首先从一系列嫌疑人入手,经过排除(花匠有当时不在现场的合理证词)和提炼(罪犯多是红色头发)的过程,判断出管家可能犯了罪。演绎的步骤以下:
(1)列举出全部可能的缘由或假设
第1步是创建1份全部想象获得的错误线索的清单,线索不须要有完整的解释;它们纯粹是一些推测,帮助咱们组织和分析现有的数据。
(2)利用数据排除可能的缘由
详细检查全部的数据,尤为寻找存在矛盾的地方,而后尽可能排除全部可能的缘由,仅留下1条。若是全部的缘由都排除掉了,须要增长额外的测试用例,获得更多的数据来设计新的推测。若是剩下的缘由多余1个,那么首先选择最有可能的缘由,即主要假设。
(3)提炼剩下的假设
此时的可能缘由也许是正确的,但可能不够具体,不能指出错误来。所以,须要使用现有的线索来提炼这个准则。举例来讲,咱们可能首先想到“对文件中最后一行的处理可能存在错误”,将其更加具体化,提炼为“对文件最后一行的文件结束指示器的处理,可能存在错误”。
(4)证实剩下的假设
这个重要步骤与概括法中的第4个步骤相同。
在小程序中定位错误的一种有效方法是沿着程序的逻辑结构回溯不正确的结果,直到找到程序逻辑出错的位置。也即,从程序产生不正确结果的地方开始,从该处观察到的结果推断出程序变量应该是些什么值。在头脑中,从这个位置开始逆向执行程序,重复使用“若是程序在此处的状态时这样的,那么程序在上面位置的状态就必然是那样的”过程,就能很快定位出错误。
适用对象:小程序;代码很是独立的模块等。
最后一个“思惟型”的调试方法是使用测试用例。考虑下面两种类型的测试用例:供测试的测试用例,其目的是暴露出之前还没有发现的错误;供调试的测试用例,其目的是提供有用的信息,供定位某个被怀疑的错误之用。换句话说,当发现了某个被怀疑的错误的症状以后,咱们须要编写与原先有所变化的测试用例,尽可能肯定错误的位置。
实际上,这种方法不是一个彻底独立的方法;它经常结合概括法一块儿使用,以得到进行假设和/或证实假设所需的信息。它能够和演绎法一块儿使用,以排除嫌疑的缘由,提炼剩下的假设,并/或证实假设。
若是,曾经遇到过相似错误,或者有过相似经验,建议使用演绎法。
↓↓
若是错误历来没有遇到过,请使用概括法。
↓↓
下列场合,建议使用回溯法
1)程序比较小
2)模块比较独立
3)已初步知道出错代码位置(经过查看log等,已初步知道代码范围)
↓↓
若是,概括法、演绎法、回溯法都没法奏效,请使用暴力法(死马当活马医)。此时,请结合对分查找法,这样能够提升效率0.5~1倍。
附录
为方便打印和查看,作一个debugging 总体图。