软件测试技术与质量保证05,.ppt
软件测试技术与软件测试技术与质量保证质量保证主讲人:徐丽主讲人:徐丽第第2 2章章 程序调试技术程序调试技术 程程序序调调试试是是在在进进行行了了成成功功的的测测试试之之后后才才开开始始的的工工作作。它它与与软软件件测测试试不不同同,软软件件测测试试的的目目的的是是尽尽可可能能多多地地发发现现软软件件中中的的错错误误,但但进进一一步步诊诊断断和和改改正正程程序序中中潜潜在在的的错错误误,则是调试的任务。则是调试的任务。调试活动由两部分组成:调试活动由两部分组成:确确定定程程序序中中可可疑疑错错误误的的确确切切性质和位置。性质和位置。对对程程序序(设设计计,编编码码)进进行行修改,排除这个错误。修改,排除这个错误。通通常常,调调试试工工作作是是一一个个具具有有很很强强技技巧巧的的工工作作。软软件件运运行行失失效效或或出出现现问问题题,往往往往只只是是潜潜在在错错误误的的外外部部表表现现,而而外外部部表表现现与与内内在在原原因因之之间间常常常常没没有有明明显显的的联联系系。如如果果要要找找出出真真正正的的原原因因,排排除除潜潜在在的的错错误误,不不是是一一件件易易事事。因因此此可可以以说说,调调试试是是通通过过现现象象找找出出原原因因的一个思维分析的过程。的一个思维分析的过程。2.1 2.1 调试步骤调试步骤调试的执行步骤如下:调试的执行步骤如下:从从错错误误的的外外部部表表现现形形式式入入手手,确定程序中出错位置;确定程序中出错位置;研研究究有有关关部部分分的的程程序序,找找出出错误的内在原因;错误的内在原因;修修改改设设计计和和代代码码,以以排排除除这这个错误;个错误;重重复复进进行行暴暴露露了了这这个个错错误误的的原原始测试或某些有关测试,以确认:始测试或某些有关测试,以确认:该错误是否被排除;该错误是否被排除;是否引进了新的错误。是否引进了新的错误。如果所作的修改无效,则撤销如果所作的修改无效,则撤销这次改动,重复上述过程,至到找到这次改动,重复上述过程,至到找到一个有效的解决方法为止。一个有效的解决方法为止。调试是一个相当艰苦的过程,究其原因调试是一个相当艰苦的过程,究其原因除了开发人员心理方面的障碍外,还因为隐除了开发人员心理方面的障碍外,还因为隐藏在程序中的错误具有下列特殊的性质:藏在程序中的错误具有下列特殊的性质:错误的外部征兆远离引起错误的内部错误的外部征兆远离引起错误的内部原因,对于高度耦合的程序结构此类现象更原因,对于高度耦合的程序结构此类现象更为严重;为严重;纠正一个错误造成了另一错误现象纠正一个错误造成了另一错误现象(暂时)的消失;(暂时)的消失;某些错误征兆只是假象;某些错误征兆只是假象;因操作人员一时疏忽造成的某些错误因操作人员一时疏忽造成的某些错误征兆不易追踪;征兆不易追踪;错误是由于时序问题而不是程序错误是由于时序问题而不是程序引起的;引起的;输入条件难以精确地再构造(例输入条件难以精确地再构造(例如,某些实时应用的输入次序不确定);如,某些实时应用的输入次序不确定);错误征兆时有时无,此现象对嵌错误征兆时有时无,此现象对嵌入式系统尤其普遍;入式系统尤其普遍;错误是由于把任务分布在若干台错误是由于把任务分布在若干台不同处理机上运行而造成的。不同处理机上运行而造成的。2.2 2.2 调试原则调试原则因因为为调调试试有有两两部部分分组组成成,所所以以调调试试原原则也分成两组。则也分成两组。1 1、确定错误的性质和位置的原则、确定错误的性质和位置的原则 用用头头脑脑去去分分析析思思考考与与错错误误征征兆兆有有关关的的信信息息。最最有有效效的的调调试试方方法法是是用用头头脑脑分分析析与与错错误误征征兆兆有有关关的的信信息息。一一个个能能干干的的程程序序调调试试员员应应能能做做到到不不使使用用计算机就能够确定大部分错误。计算机就能够确定大部分错误。避开死胡同。避开死胡同。如如果果程程序序调调试试员员走走进进了了死死胡胡同同,或或者者陷陷入入了了绝绝境境,最最好好暂暂时时把把问问题题抛抛开开,留留到到第第二二天天再再去去考考虑虑,或或者者向向其其他他人人讲讲解解这这个个问问题题。事事实实上上常常有有这这种种情情形形:向向一一个个好好的的听听众众简简单单的的描描述述这这个个问问题题时时,不不需需要要任任何何听听讲讲者者的的提提示示,你你自自己会突然发现问题的所在。己会突然发现问题的所在。只只把把调调试试工工具具当当作作辅辅助助手手段段来来使使用用。利利用用调调试试工工具具,可可以以帮帮助助思思考考,但但不不能能代代替替思思考考。因因为为调调试试工工具具给给你你的的是是一一种种无无规规律律的的调调试试方方法法。实实验验证证明明,即即使使是是对对一一个个不不熟熟悉悉的的程程序序进进行行调调试试时时,不不用用工工具具的的人人往往往往比比使使用用工工具的人更容易成功。具的人更容易成功。避避免免用用试试探探法法,最最多多只只能能把把它它当当作作最最后后手手段段。初初学学调调试试的的人人最最常常犯犯的的一一个个错错误误是是想想试试试试修修改改程程序序来来解解决决问问题题。这这还还是是一一种种碰碰运运气气的的盲盲目目行行为为,它它的的成成功功机机会会很很小小,而而且还常把新的错误带到问题中来。且还常把新的错误带到问题中来。2 2修改错误的原则修改错误的原则 在在出出现现错错误误的的地地方方,很很可可能能还还有有别别的错误。的错误。经经验验证证明明,错错误误有有群群集集现现象象,当当在在某某一一程程序序段段发发现现有有错错误误时时,在在该该程程序序段段中中还还存存在在别别的的错错误误的的概概率率也也很很高高。因因此此,在在修修改改一一个个错错误误时时,还还要要查查一一下它的近邻,看是否还有别的错误。下它的近邻,看是否还有别的错误。修修改改错错误误的的一一个个常常见见失失误误是是只只修修改改了了这这个个错错误误的的征征兆兆或或这这个个错错误误的的表表现,而没有修改错误的本身。现,而没有修改错误的本身。如如果果提提出出的的修修改改不不能能解解释释与与这这个个错错误误有有关关的的全全部部线线索索,那那就就表表明明了了只修改了错误的一部分。只修改了错误的一部分。当当心心修修改改一一个个错错误误的的同同时时又又可可能能会会引入新的错误。引入新的错误。人人们们不不仅仅需需要要注注意意不不正正确确的的修修改改,而而且且还还要要注注意意看看起起来来是是正正确确的的修修改改可可能能会会带带来来的的副副作作用用,即即引引进进新新的的错错误误。因因此此在在修修改改了了错错误误之之后后,必必须须进进行行回回归测试,以确定是否引进了新的错误。归测试,以确定是否引进了新的错误。修修改改错错误误的的过过程程将将迫迫使使人人们们暂暂时时回到程序设计阶段。回到程序设计阶段。修修改改错错误误也也是是程程序序设设计计的的一一种种形形式式。一一般般说说来来,在在程程序序设设计计阶阶段段所所使使用用的的任任何何方方法法都都可可以以应应用用到到错错误误修修正正的的过过程中来。程中来。2.3 2.3 调试方法调试方法 调试的关键在于推断程序内调试的关键在于推断程序内部的错误位置及原因。为此,可部的错误位置及原因。为此,可以采用以下几种主要的方法:以采用以下几种主要的方法:1 1普查法普查法 依依靠靠系系统统的的调调试试跟跟踪踪工工具具,或或将将信信息息打打印印或或显显示示出出来来,进进行行普普遍遍的的查查找找错错误误的的原原因因,并并进进行行排排错错的的过过程程方方法法。虽虽然然最最终终能能导导致致排排错错成成功功,但但工工作作量量太太大大,时时间间太太浪浪费费,缺缺乏乏分分析析和和高高效效率率,一一般般在在毫毫无无办办法法,迫迫不不得得已已的的时时候才用。候才用。2 2回溯法回溯法 这这是是在在小小程程序序中中常常用用的的一一种种有有效效的的排排错错方方法法。一一旦旦发发现现错错误误,人人们们先先分分析析错错误误征征兆兆,确确定定最最先先发发现现“症症状状”的的位位置置。然然后后,人人工工沿沿程程序序的的控控制制流流程程,向向回回追追踪踪源源程程序序代代码码,直直到到找找到到错错误误根根源源或或确确定定错错误误产产生生的的范范围围。即即在在其其状状态态是是预预期期的的点点与与第第一一个个状状态态不不是预期的点之间的程序位置。是预期的点之间的程序位置。回回溯溯法法对对于于小小程程序序很很有有效效,往往往往能能把把错错误误范范围围缩缩小小到到程程序序中中的的一一小小段段代代码码,仔仔细细分分析析这这段段代代码码不不难难确确定定出出错错的的准准确确位位置置。但但对对于于大大程程序序,由由于于回回溯溯的的路路径径数数目目较较多多,回回溯溯会会变得很困难。变得很困难。3 3归纳法归纳法 归归纳纳法法是是由由测测试试取取得得错错误误数数据据的的个个别别数数据据,分分析析组组织织出出一一般般可可能能的的错错误误线线索索,研研究究出出错错规规律律的的线线索索关关系系,由由此此设设置置错错误误原原因因,证证明明设设置置错错误误原原因因,能能证证明明就就排排除除错错误误,不不能能证证明明说说明明分分析析的的不不准准,说说明明出出错错规规律律的的线线索索关关系系不不正正确确,再再重重新新选选择择相相应应测测试试数数据据,如此周而复始的进行。如此周而复始的进行。可可见见归归纳纳法法是是一一种种由由特特殊殊到到一一般般的的错错误误推推断断排排除除法法。简简而而言言之之其其过过程程是是收收集集有有关关数数据据,组组织织数数据据,寻寻找找假假设设,证证明明假假设设,排排除除假假设设错误的过程。错误的过程。4 4演绎法演绎法 演绎法是一种从一般原理或前提出演绎法是一种从一般原理或前提出发,经过排除和精化过程推导出结论的发,经过排除和精化过程推导出结论的思考方法。演绎法排错是测试人员首先思考方法。演绎法排错是测试人员首先根据已有的测试用例,设想及枚举出所根据已有的测试用例,设想及枚举出所有可能出错的原因作为假设;然后再用有可能出错的原因作为假设;然后再用原始测试数据或新的测试,从中逐个排原始测试数据或新的测试,从中逐个排除不可能正确的假设;最后,再用测试除不可能正确的假设;最后,再用测试数据验证余下的假设确是出错的原因。数据验证余下的假设确是出错的原因。上述每一类方法均可辅以调试工具。目上述每一类方法均可辅以调试工具。目前,调试编译器、动态调试器(前,调试编译器、动态调试器(“追踪器追踪器”)、测试用例自动生成器、存储器映象及)、测试用例自动生成器、存储器映象及交叉访问示图等到一系列工具已广为使用。交叉访问示图等到一系列工具已广为使用。然而,无论什么工具也替代不了一个开发人然而,无论什么工具也替代不了一个开发人员在对完整的设计文档和清晰的源代码进行员在对完整的设计文档和清晰的源代码进行认真审阅和推敲之后所起的作用。此外,不认真审阅和推敲之后所起的作用。此外,不应荒废调试过程中最有价值的一个资源,那应荒废调试过程中最有价值的一个资源,那就是开发小组中其他成员的评价和忠告,正就是开发小组中其他成员的评价和忠告,正所谓所谓“当事者迷,旁观者清当事者迷,旁观者清”。前面多次提到,修改一处老问题可能引入前面多次提到,修改一处老问题可能引入几处新问题,有时程序越改越乱,但若能几处新问题,有时程序越改越乱,但若能做到每次纠错前都扪心自问三个问题,情做到每次纠错前都扪心自问三个问题,情况将大为改观:况将大为改观:导致这个错误的原因在程序其他部分还导致这个错误的原因在程序其他部分还可能存在吗?可能存在吗?本次修改可能对程序中相关的逻辑和数本次修改可能对程序中相关的逻辑和数据造成什么影响?引起什么问题?据造成什么影响?引起什么问题?上次遇到的类似问题是如何排除的?上次遇到的类似问题是如何排除的?2.4 2.4 C C程序实用调试技巧程序实用调试技巧1 1如果运行的程序挂起了,应该怎么办?如果运行的程序挂起了,应该怎么办?当当你你运运行行一一个个程程序序时时会会有有多多种种原原因因使使它它挂挂起,这些原因可以分为以下起,这些原因可以分为以下4 4种基本类型:种基本类型:程序中有死循环;程序中有死循环;程序运行的时间比所期望的长;程序运行的时间比所期望的长;程程序序在在等等待待某某些些输输入入信信息息,并并且且知知道道输入正确后才会继续运行;输入正确后才会继续运行;程程序序设设计计的的目目的的就就是是为为了了延延迟迟一一些些时时间,或者暂停执行。间,或者暂停执行。死循环死循环 当你的程序出现了死循环当你的程序出现了死循环时,机器将无数次地执行同一段代码,时,机器将无数次地执行同一段代码,这种操作当然是程序员所不希望的。这种操作当然是程序员所不希望的。出现死循环的原因是程序员使程序进出现死循环的原因是程序员使程序进行循环的判断条件永远为真,或者使行循环的判断条件永远为真,或者使程序退出循环的判断条件永远为假。程序退出循环的判断条件永远为假。运行时间比期望的时间长运行时间比期望的时间长 在有些情况下,你会发现程序并在有些情况下,你会发现程序并没有被完全没有被完全 锁死锁死,只不过它的运行,只不过它的运行时间比你所期望的时间长,这种情况时间比你所期望的时间长,这种情况是令人讨厌的。如果你所使用的计算是令人讨厌的。如果你所使用的计算机运算速度很快,能在极短的时间内机运算速度很快,能在极短的时间内完成很复杂的运算,那么这种情况就完成很复杂的运算,那么这种情况就更令人讨厌了。更令人讨厌了。等待正确的输入等待正确的输入 有时程序停止运行是因为有时程序停止运行是因为它在等待正确的输入信息。最简单的它在等待正确的输入信息。最简单的情况就是程序在等待用户输入信息,情况就是程序在等待用户输入信息,而程序却没有输出响应的提示信息,而程序却没有输出响应的提示信息,因而用户不知道要输入信息,程序看因而用户不知道要输入信息,程序看上去就好象锁住了。更令人讨厌的是上去就好象锁住了。更令人讨厌的是由缓冲造成的这种结果。由缓冲造成的这种结果。2 2、用什么办法才能找出程序中的错误、用什么办法才能找出程序中的错误?在调试程序的过程中,程序员应该记住以在调试程序的过程中,程序员应该记住以下几种技巧:下几种技巧:先调试程序中较小的组成部分,然后先调试程序中较小的组成部分,然后调试较大的组成部分调试较大的组成部分 如果你的程序编写得很好,那么它将包含如果你的程序编写得很好,那么它将包含一些较小的组成部分,最好先证实程序的一些较小的组成部分,最好先证实程序的这些部分是正确的。尽管程序中的错误并这些部分是正确的。尽管程序中的错误并不一定发生在这些部分中,但是先调试它不一定发生在这些部分中,但是先调试它们有助于你理解程序的总体结构,并且证们有助于你理解程序的总体结构,并且证实程序的那些部分不存在错误。实程序的那些部分不存在错误。彻彻底底调调试试好好程程序序的的一一个个组组成成部部分分后后,再再调试下一个组成部分调试下一个组成部分 这这一一点点非非常常重重要要。如如果果证证实实了了程程序序的的一一个个组组成成部部分分是是正正确确的的,不不仅仅能能缩缩小小可可能能存存在在错错误误的的范范围围,而而且且程程序序的的其其它它组组成成部部分分就就能能安安全全地地使使用用这这部部分分程程序序了了。这这里里应应用用了了一一种种很很好好的的经经验验性性原原则则,简简单单地地说说就就是是调调试试一一段段代代码码的的难难度度与与这这段段代代码码长长度度的的平平方方成成正正比比,因因此此,调调试试一一段段2020行行的的代代码码比比调调试一段试一段1010行的代码难行的代码难4 4倍。倍。连续地观察程序流和数据的变化连续地观察程序流和数据的变化 这这一一点点也也很很重重要要!如如果果你你小小心心仔仔细细地地设设计计和和编编写写程程序序,那那么么通通过过监监视视程程序序的的输输出出你你就就能能准准确确地地知知道道正正在在执执行行的的是是哪哪部部分分代代码码以以及及各各个个变变量量的的内内容容都都是是什什么么。当当然然,如如果果程程序序表表现现不不正正常常,你你就就无无法法做做到到这这一一点点。为为了了做做到到这这一一点点,通通常常只只能能借借助助于于调调试试程程序序或或者者在在程程序序中中加加入入大大量量的的printprint语语句句来观察控制流和重要变量的内容。来观察控制流和重要变量的内容。始始终终打打开开编编译译程程序序警警告告选选项项,并并试试图图消消除所有警告除所有警告 在在开开发发程程序序的的过过程程中中,你你自自始始至至终终都都要要做做到到这这一一点点,否否则则,你你就就会会面面临临一一项项十十分分繁繁重重的的工工作作,尽尽管管许许多多程程序序员员认认为为消消除除编编译译程程序序警警告告是是一一项项繁繁琐琐的的工工作作,但但它它是是很很有有价价值值的的。编编译译程程序序给给出出警警告告的的大大部部分分代代码码至至少少都都是是有有问问题题的的,因因此此用用一一些些时时间间把把它它们们变变成成正正确确的的代代码码是是值值得得的的,而而且且,通通过过消消除除这这些些警警告告,你你往往往往会会找找到到程程序序中中真真正发生错误的地方。正发生错误的地方。准确地缩小存在错误的范围准确地缩小存在错误的范围 如如果果你你能能一一下下子子确确定定存存在在错错误误的的那那部部分分程程序序并并在在其其中中找找到到错错误误,那那就就会会节节省省许许多多调调试试时时间间。但但事事实实上上,我我们们并并不不能能总总是是一一下下子子就就命命中中要要害害,因因此此,通通常常的的做做法法是是逐逐步步缩缩小小可可能能存存在在错错误误的的程程序序范范围围,并并通通过过这这种种过过程程找找出出真真正正存存在在错错误误的的那那部部分分程程序序。当当你你找找到到这这部部分分程程序序后后,就就可可以以把把所所有有的的调试工作集中到这部分程序上了。调试工作集中到这部分程序上了。2.5 2.5 VcVc+6.0+6.0常见编译错误说明常见编译错误说明error error C2143:C2143:syntax syntax error error:missing missing;before identifier;before identifier scanfscanf 语法错误:在语法错误:在 scanfscanf 之前缺少之前缺少“;”出错实例:出错实例:intint n n scanf scanf(“%d”,&n);(“%d”,&n);error error C2065:C2065:A A:undeclared undeclared identifier identifier 标识符标识符A A未声明未声明出错实例:出错实例:intint a;a;scanf scanf(“%d”,&A);(“%d”,&A);error error C2065:C2065:sqrtsqrt :undeclared identifierundeclared identifier标标识识符符sqrtsqrt未未定定义义(原原因因:未未包包含含 math.h)error error C2296:C2296:%:illegal,illegal,left left operand has type floatoperand has type floaterror error C2297:C2297:%:illegal,illegal,right right operand has type floatoperand has type float 非法非法,%,%运算符的左右操作数不是整型运算符的左右操作数不是整型 出错实例:出错实例:float a=10,b=2;float a=10,b=2;printf printf(“%dn”,a%b);(“%dn”,a%b);error error C2440:C2440:=:cannot cannot convert convert from char 2 to charfrom char 2 to char 不能将字符串转换成字符不能将字符串转换成字符 出错实例:出错实例:char char chch2;2;chch=“a”;=“a”;error error C2181:C2181:illegal illegal else else without without matching ifmatching if else else子句没有匹配的子句没有匹配的ififerror error C2196:C2196:case case value value 1 1 already already usedused case case 1:1:已已经经被被使使用用(原原因因:有有相相同同的的case case 常量)常量)error C2078:too manyerror C2078:too many initializers initializers 初始化值太多初始化值太多 出出错错实实例例:intint a5 a5=1,1,2,2,3,3,4,4,5,6;5,6;error C2105:+needs l-valueerror C2105:+needs l-value +运运算算只只能能作作用用于于左左值值(常常量量不不能能作作左左值)值)出错实例:出错实例:int int a=1,2,3,4,5;a=1,2,3,4,5;int int *pa=+a;*pa=+a;/数数组组名名是地址常量,不能作左值是地址常量,不能作左值error error C2117:C2117:Hello Hello:array array bounds bounds overflowoverflow 数组边界溢出数组边界溢出 出错实例:出错实例:charchar str str5=Hello;5=Hello;error error C4716:C4716:Function Function:must must return a valuereturn a value 函数函数FunctionFunction()()必须返回一个值必须返回一个值 出错实例:出错实例:intint Function(Function(intint n)n)/函数定义,返回值类型:函数定义,返回值类型:int int 类型类型 return;return;/不不返返回回值值,语语法法错错误误,应应为为:return return 表达式表达式;error error C2562:C2562:Function Function:void void function returning a valuefunction returning a value 无无返返回回值值的的函函数数FunctionFunction()返返回回了了一一个值个值 出错实例:出错实例:void Function(void Function(intint n)n)/函数定义,无返回值函数定义,无返回值 return(n%10);return(n%10);/有返回值,语法错误,应为:有返回值,语法错误,应为:return;return;error error C2447:C2447:missing missing function function header(old-style formal list?)header(old-style formal list?)缺少函数头缺少函数头出出错错实实例例:void void Function(Function(intint n);n);/都是都是“;”惹的祸惹的祸 return;return;error error C2082:C2082:redefinition redefinition of of formal parameter nformal parameter n 重定义了参数重定义了参数nn 出出错错实实例例:void void Function(Function(intint n)n)intint m,n;m,n;/局部变量局部变量n n与参数与参数n n同名同名 return;return;error error C2601:C2601:Function Function:local local function function definitions definitions are are illegalillegal 函函数数FunctionFunction()定定义义是是非非法法的的(原原因:出现嵌套定义)因:出现嵌套定义)error error C2065:C2065:Function Function:undeclared identifierundeclared identifier 函函数数FunctionFunction()未未声声明明(原原因因:先先使用后声明或定义)使用后声明或定义)