《软件测试的艺术》第八章 调试
-
- 8.0 前言
- 8.1 暴力法调试
- 8.2 归纳法调试
- 8.3 演绎法调试
- 8.4 回溯法调试
- 8.5 测试法调试
- 8.6 调试的原则
-
- 8.6.1 定位错误的原则
- 8.6.2 修改错误的技术
- 8.7 错误分析
- 8.8 小结
8.0 前言
简单地讲,调试是执行一次成功的测试之后所要进行的工作。调试是一个包含两个步骤的过程,从执行了一个成功的测试用例、发现了一个问题之后开始。第一步,确定程序中可疑错误的准确性质和位置;第二步,修改错误。
8.1 暴力法调试
暴力调试法可至少分为三种类型:
- 利用内存信息输出来调试。
该类型使用内存信息输出(通常使用十六进制或八进制格式粗略地显示所有的存储区域)是最缺乏效率的暴力调试方法,原因如下:
- 难以在内存区域与源程序中的变量之间建立对应关系。
- 即使对于复杂程度较低的程序,内存信息输出也会产生数量非常庞大的数据,其中的大多数都是与调试无关的。
- 内存信息输出显示的是程序的静态快照,仅能显示出在某一个时刻程序的状态;为了发现错误,还需要研究程序的动态状态。
- 内存信息输出很少可以精确地在错误发生的地方产生,因此无法显示在错误发生时程序的状态。错误发生到输出内存信息这段时间之内程序执行的活动,可能会掩盖掉发现错误所需的线索。
- 通过分析输出的内存信息来发现问题的方法并不太多。
- 根据一般的“在程序中插入打印语句”建议来调试。
第二种类型,在失效的程序中插入输出变量值的语句,这种做法也不具有很强的优势。它可能比内存信息输出要好一些,因为可以显示程序的动态状态,让我们检查的信息可以相对容易地与源程序联系起来。但是这种方法同样也有很多缺点:
- 它不是鼓励我们去思考程序中的问题,而主要是一种碰运气的方法。
- 它所产生的需要分析的数据量非常庞大。
- 它要求我们修改程序,这些修改可能会掩盖掉错误、改变关键的时序关系,或者会引入新的错误。
- 它可能对小型程序有效,但如果应用到大型程序,成本就相当高。况且对于某些类型的程序,如操作系统或过程控制软件,这种办法甚至无法使用。
- 使用自动化的调试工具进行调试。
第三种类型,自动化调试工具的工作机制类似于在程序中插入打印语句,但并不是修改程序本身。可以使用编程语言的调试功能,或使用特殊的交互式调试工具来分析程序的动态状态。可能会用到的语言功能有:产生可打印的语句执行轨迹的机制、子程序调用以及/或者对特定变量的修改等。调试工具的一个共同功能是可以设置断点,使程序在执行到某条特定语句或改动了每个特定变量的值时暂停执行,然后程序员就可以检查程序当前的状态。同样,这种方法也主要是在碰运气,常常会生成数量过于庞大的无关数据。
这些暴力调试方法的主要问题在于:它们都忽略了思考的过程。我们建议仅在下列情况下使用暴力调试方法:(1)其他的方法都失败了;(2)作为我们下面将会讨论的思考过程的补充,而不是替代方法。
8.2 归纳法调试
归纳调试的步骤如下:
作出假设。下一步是研究线索之间的联系,利用线索结构里可能的模式作出一个或多个关于错误原因的假设。如果还无法作出推测,就需要更多的数据。如果可能有多个假设存在,首先选择最有可能的一个。8.3 演绎法调试
演绎的过程是从一些普遍的理论或前提出发,使用排除或精炼的过程,达到一个结论(错误的位置),如下图所示:

演绎的步骤如下:
- 列举出所有可能的原因或假设。第一步是建立一份所有想象得到的错误线索的清单,线索不需要有完整的解释;它们纯粹是一些推测,帮助我们组织和分析现有的数据。
- 利用数据排除可能的原因。详细检查所有的数据,尤其在寻找存在矛盾的地方,然后尽量排除所有可能的原因,仅留下一条。如果所有的原因都排除掉了,需要增加额外的测试用例,得到更多的数据来设计新的推测。如果剩下的原因多于一个,那么首先选择最有可能的原因,即主要假设。
- 提炼剩下的假设。此时的可能原因也许是正确的,但可能不够具体,不能指出错误来。因此,下一步是使用现有的线索来提炼这个推测。
- 证明剩下的假设。
- 修复问题。
8.4 回溯法调试
在小型程序中定位错误的一种有效方法是沿着程序的逻辑结构回溯不正确的结果,直到找出程序逻辑出错的位置。换句话说,从程序产生不正确结果的地方开始,从该处观察到的结果推断出程序变量应该是些什么值。使用这个过程,可以确定程序中从状态符合预期值的位置点,到第一个不符合预期值的位置点之间的范围。
8.5 测试法调试
当发现了某个被怀疑的错误的症状之后,我们需要编写与原先有所变化的测试用例,尽量确定错误的位置。实际上,这种方法不是一个完全独立的方法;它常常结合归纳法一起使用,以获得进行假设和/或证明假设所需的信息。它也可以和演绎法一起使用,以排除有嫌疑的原因,提炼剩下的假设,并/或证明假设。
8.6 调试的原则
8.6.1 定位错误的原则
- 动脑筋。
最为有效的调试方法是动脑筋对错误症状的有关信息进行分析。在这里列出一些思考的诀窍:
- 让自己置身于安静、没有干扰的环境之中,这些干扰通常指同事的谈话声、打电话、手机铃声以及其他潜在的干扰因素,确保这些不会分散你的注意力。
- 不看代码,在脑海中思考程序是怎么设计的,并思考表现异常的地方本应该是什么样的。
- 把注意力集中在思考程序正确行为的过程上,并想象那些可能导致错误设计的代码实现方式。
- 如果遇到了僵局,就留到稍后解决。
- 如果遇到了困境,就把问题描述给其他人听。
- 仅将调试工具作为第二种手段。
- 避免使用试验法——仅将其作为最后的手段。
8.6.2 修改错误的技术
- 存在一个缺陷的地方,很有可能还存在其他缺陷。
- 应纠正错误本身,而不仅是其症状。
- 正确纠正错误的可能性并非100%。
- 随着程序规模的增加,正确修改错误的可能性反而降低。
- 应意识改正错误会引入新错误的可能性。
- 修改错误的过程也是临时回到设计阶段的过程。
- 应修改源代码,而不是目标代码。
8.7 错误分析
调试除了有消灭程序中错误的价值之外,还有其他重要作用:它可以告诉我们软件错误的一些本质,我们对此了解得非常之少。关于软件错误本质的信息可以为改进将来的设计、编码和测试过程提供有价值的反馈信息。
详细的错误分析会包括如下内容:
- 错误出现在什么地方求我们指出错误的源头和发生时间。
- 谁制造了这个错误这不是为了处罚某人,而是为了进行培训。
- 哪些做得不正确是由于某人写得不清楚于某人缺乏对编程语言的培训字错误做得不对因为没有考虑有效输入li>
- 如何避免该错误的出现一个项目中可以进行哪些调整以避免该问题的出现题的答案是我们所寻找的最为宝贵的反馈信息或知识。
- 为什么错误没有早些发现li>
- 该如何更早地发现错误问题的答案是另一个宝贵的反馈信息。该如何改进评审和测试过程以便在将来的项目中更早地发现同类型的错误li>
8.8 小结
这一章我们涉猎了软件调试的一些重要知识。我们从最不理想的方法——暴力调试讲起,暴力调试通常需要使用内存快照分析技术、在程序中插入打印语句或者自动化工具等。暴力调试方法或许能够帮你找到错误,但这并不是最有效率的调试方法。
之后我们示范了如何从研究错误症状或线索起步,继而进行全局归纳分析(归纳调试法)。另一种调试的常用技术是排除法,通过逐层分析,抽丝剥茧,最后完成错误定位(演绎法调试)。我们还介绍了回溯法调试,从程序错误的地方往前回溯,直到错误发源地。最后我们讨论了测试法调试。
然后,如果我们只能提供一条行之有效的调试方法,回顾本章总结的一些调试原则,都有一个共同的方法:那就是“思考!”。通过这些原则对错误进行思考,才能向着精确和高校调试的道路上迈进。不过这一切的基础都构建在你对程序本身的了解和掌握程度上。不要禁锢你的思维,打开它,听从它对你经验的调度,让你的知识和潜意识引导你走向最终错误定位之路。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!