1.1 软件测试的产生
随着软件技术的迅速发展和广泛深入地应用于人类社会和生活的各个领域,随着软件系统的规模和复杂性与日俱增,软件的生产成本和软件中存在的缺陷和故障造成的各类损失也在不断发生,其错误产生的概率正在大大增加,甚至有时会带来灾难性的后果。软件的质量问题已成为所有应用软件和开发软件的人们关注的焦点,由于软件是人脑的高度智能化的具体体现,不同于其他科技与生产领域,因此软件与生俱来就有可能存在缺陷或故障,并难以根除。如何防止和减少软件可能存在的问题呢?答案是进行软件测试。软件测试是最有效的排除和防止软件缺陷与故障的手段,通过加强软件测试来控制质量,通过修正缺陷来提高软件产品的质量。软件测试的产生和运用促进了软件测试的学科理论与技术运用的快速发展,新的软件测试理论、新的软件测试策略与方法、新的软件测试技术在不断地涌出和创新。软件测试已成为软件行业中的专门学科,软件测试的运用与实践也在蓬勃发展,软件企业中的软件测试专门组织和机构诞生和发展起来,同时伴随产生的是软件测试工作的专业化和职业化。
1.1.1 软件可靠性问题
自计算机软件产生以来,研究表明,因软件的错误和故障而引发的系统失效、崩溃,产生的错误与因计算机硬件故障而引发的系统失效的比例大约是10:1。现代社会对计算机系统的需求迅速增加,而计算机系统的“灵魂”是软件。人类对计算机的依赖程度越高,对其可靠性的要求也就越高。实验数据表明,目前在各类计算机中运行的软件留存的故障密度,对于要求很高的金融系统中的关键财务软件为每千行代码1~10个缺陷或故障,对于关键的各种相关生命系统中的软件,如医学计算系统、载人航天等计算系统等,为每千行代码0.01~1个缺陷或故障。与之相比,其他领域对可靠性要求相对较低的各类软件系统,其缺陷或故障的存在就更多了。因此,计算机科学家和软件工程师们一直在不断努力地改善软件的这种状况,而正是由于软件系统的可靠性大幅度地提高,才使得计算机系统能够广泛而深入地应用于人类社会生活的各个方面。
一个可靠的软件系统应该是正确、完整、一致和健壮的,也是软件用户所期望的。IEEE组织将软件的可靠性定义为:系统在特定环境下,在给定的时间内无故障运行的概率。因此,软件可靠性是对软件在设计、开发以及所预定的环境下具有特定能力的置信度的一个度量,它是衡量软件质量的主要参数。软件测试是保证软件质量,提高软件可靠性的最重要的策略与手段。目前,软件测试在整个软件开发周期中所占的比例日益上升,许多软件开发组织已将软件开发所耗费的资源的40%用于测试。对于高可靠性的软件系统,如飞行控制、军事武器系统、核反应堆控制、金融软件、生命科学、医疗诊断等,其软件测试费用是软件开发其他阶段所耗费的费用总和的数倍。
人类从很早就认识到一条规律,即不能仅仅依靠自己来检查自己的工作或产品,必须由他人来监督和检查,以保证客观性。由于软件产品的“特殊”性质,其可靠性必须依赖于软件测试这个环节与过程。
1.1.2 软件缺陷与故障
1.软件缺陷和故障案例
当今人类的生存和发展已经离不开各种各样的信息服务,为了获取这些信息,需要计算机网络或通信网络的支撑,这里不仅包含计算机硬件设备,还包括各种功能和用途的计算机软件,软件无处不在。然而,软件是由人编写开发的,是一种逻辑思维的产品,尽管现在软件开发当中采取了一系列的有效措施,能不断地提高软件产品的质量,但仍然无法完全避免软件会存在各种各样的缺陷。
软件故障或缺陷,依据其可能造成的危害程度不同,分为轻、重等不同级别。通过下面几例软件缺陷和故障的案例分析,足以说明软件缺陷和故障问题造成的严重损失和灾难。
【案例1】美国迪斯尼公司生产的狮子王游戏软件bug事件,这是一项典型的软件兼容性缺陷问题。1994年,该公司发布面向青少年的游戏软件“狮子王动画故事书”,销售异常火爆,使该游戏软件几乎成为当年秋季全美青少年必买的游戏软件产品。但产品售后不久,客户支持部投诉电话就一直不断,愤怒的儿童家长和玩不成游戏的孩子们大量投诉该游戏软件的缺陷,一时间报纸和电视媒体大量报道了这一游戏软件的各种问题,使该公司的声誉大损,并为改正软件缺陷和故障付出了沉重的代价。后经调查证实,造成这一严重问题的原因是迪斯尼公司没有对该游戏软件在已投入市场上使用的各种PC机型上进行完整的测试,游戏软件对硬件环境的兼容性没有得到保障,虽然该游戏软件在软件工程师们的机器硬件系统上工作正常,但在大众群体使用的系统中却存在不兼容的问题。
【案例2】美国航天局火星极地飞船着陆事故。1999年12月3日,美国航天局的火星极地着陆飞船在试图登陆火星表面时突然失踪。负责这一太空发展项目的错误修正委员会的专家们观测到这一幕并分析了事故,确定出现该事故的原因可能是由于某一数据位被意外地更改,造成灾难性后果,并得出造成事故的问题应在内部测试时就予以解决的结论。简要地说,火星极地飞船着陆过程是这样的:当飞船快要降落火星表面时,它将打开着陆降落伞以减缓飞船下落速度,在飞船距离火星1800m时,飞船将丢弃降落伞,同时点燃着陆推进器(反向推力),控制和稳定飞船的下降速度,同时飞船的三条支撑脚将迅速打开,使其在剩余的高度里缓慢降落到火星表面,在预定地点着陆。然而为节省研制经费,简化了确定何时关闭着陆推进器的自动装置,由通常太空船使用的昂贵着陆雷达系统改为在飞船的支撑脚上安装简易触发开关,并在着陆程序中设置一个数据位来控制关闭着陆推进器燃料开关。显然,飞船支撑腿在没有着地之前,推进器引擎将一直处于着火工作状态,支撑脚着地瞬间,触发开关,程序控制关闭燃料,平稳安全着陆。但遗憾的是,事后分析测试中发现,当飞船的支撑脚打开准备着陆时,机械的震动却很容易触发着地触电开关,导致程序设置了错误的数据位,关闭了着陆推进器燃料,也就是说,使得着陆推进器提前停止工作,使着陆飞船加速下坠1800m之后直接冲向了火星表面,飞船撞成碎片。这一事故后果非常严重,损失巨大,然而起因却如此简单,属于软件设计中的缺陷。事实是飞行发射之前,飞船各部位工作过程经过多个小组的测试,其中一个小组测试飞船的支撑脚落地的打开过程,另一个小组测试此后的着陆过程。前一小组没有注意到着地数据位是否已置位,因为这不属于他们负责的范围,而后一小组总是在开始测试之前重置计算机,进行数据的初始化,清除数据位。双方独立工作都很好,但从未在一起进行集成(系统)测试,使系统测试中的衔接问题故障隐藏起来,从而导致灾难性事故的发生。
【案例3】跨世纪的“千年虫”问题。这是一个著名的软件缺陷问题,20世纪末的最后几年,全球各类计算机硬件系统、软件系统和应用系统都在为2000年份时间兼容问题及与此年份相关的其他存在安全隐患的“千年虫问题”付出巨大代价。据不完全统计,1998年初全球开始进行“千年虫”问题大检查,仅在金融、保险、军事、科学、商务等领域花费大量的人力、物力对现有的各种各样的程序进行检查、修改和更正,全球耗资高达几百亿美元。
【案例4】爱国者导弹防御系统炸死自家人。美国爱国者导弹系统首次应用于海湾战争中,屡建功勋,多次成功拦截飞毛腿导弹。但确实也有几次在对抗中失利,其中一枚导弹在沙特的多哈战斗中炸死了28名美军士兵。事后,分析专家得出结论:灾难是爱国者导弹防御系统中一个软件系统的缺陷所致,一个很小的系统时钟错误积累起来可能延时14个小时,造成跟踪系统失去准确度。在多哈袭击战中,导弹系统的重要时刻被延时100多个小时,造成悲剧。
诸如上述的软件错误或漏洞绝不仅仅是这几例,类似的报告数不胜数,据美国国家标准和技术协会在2002年公布的一项关于软件缺陷引起的经济损失的报告中的数据表明,软件缺陷造成的美国经济损失达到595亿美元。
全球广泛使用的多种软件的缺陷和错误经常被用户披露或曝光,软件公司不停地发布各种修正版本和软件补丁的现象已屡见不鲜。
2.软件缺陷的定义
从上述软件故障或缺陷实例中可以看到软件发生错误时造成的灾难性危害或对用户的各种损失及影响。那么,这些事件的共同特点有哪些呢?首先,软件开发过程可能没有按照预期规则或目标要求进行;其次,软件虽然都经过测试,但并不能保证完全排除了存在(特别是潜在)的错误。对软件测试而言,其任务就是要采用科学方法和技术手段来发现软件中所隐藏的错误,找出那些不明显、难以察觉、可能很简单而细微的错误,要到达此目的,是对软件测试人员的巨大挑战。
软件问题,即软件bug。之所以称为bug,源于1945年9月美国海军编程员、编译器的发明者格雷斯·哈柏(Grace Hopper)在排查“Mark II”计算机死机原因时,发现是第70号继电器出现故障,而在其中躺着一只被继电器电死的飞蛾,他将死飞蛾用透明胶布贴在了“事件记录本”当中,并注明“第一个发线虫子(bug)的实例”。此后,人们将计算机错误戏称为虫子(bug),而将排查寻找错误的工作称为Debug。在软件工程或软件测试中都被称为软件缺陷或软件故障。在不引起误解的情况下,不管软件存在问题的规模和危害是大还是小,由于都会产生软件使用上的各种障碍,所以将这些问题统称为软件缺陷。
对于软件缺陷的精确定义,通常全球软件业界普遍认同下列5条规则:
(1)软件未达到产品说明书中已经标明的功能;
(2)软件出现了产品说明书中指明不会出现的错误;
(3)软件未达到产品说明书中虽未指出但应当达到的目标;
(4)软件功能超出了产品说明书中指明的范围;
(5)测试专业人员认为软件难以理解、不易使用,或者最终用户认为该软件使用效果不良。
为了更好地理解以上描述,这里以日常使用的计算器内的嵌入式软件来说明上述5条规则。
计算器说明书一般声称该计算器将准确无误进行加、减、乘、除运算。如果测试人员或用户选定了两个数值后,随意按下了“+”号键,结果没有任何反应,根据规则(1),这是一个软件缺陷;如果得到错误答案,根据规则(1),同样是软件缺陷。
假如计算器产品说明书指明计算器不会出现崩溃、死锁或者停止反应,而在用户随意按、敲键盘后,计算器停止接受输入或没有了反应,根据规则(2),这也是一个软件缺陷。
若在进行测试时,发现除了规定的加、减、乘、除功能之外,还能够进行求平方根的运算,而这一功能并没有在说明书的功能中规定,根据规则(3),这也是软件缺陷。
若在测试过程中发现,因电池没电而导致计算不正确,但产品说明书未能指出在此情况下应如何处理,根据规则(4),也应算做软件缺陷。
规则(5)说明了无论测试人员或者是最终用户,若发现计算器某些地方不好用,例如,按键太小、显示屏在亮光下无法看清等,也应算做软件缺陷。
3.软件缺陷的特征
大量的测试理论研究及测试实践经验的积累表明,软件缺陷的特征主要有两类:第一,软件系统的思维逻辑性与复杂性等特殊性质决定了其缺陷不易直接从肉眼看到,即“难于看到”;第二,即使在运行与使用当中发现了软件的缺陷或故障,但不易找到问题发生的原因所在,即“看到了但难于抓得到”。
4.软件缺陷产生的原因
软件测试是在软件投入运行之前,对软件需求分析、设计规格说明和编码实现的最终审定。那为什么还会产生软件缺陷呢?经过软件测试专家们的研究发现,表现在程序中的故障并不一定是由编码过程所引起的,大多数的软件缺陷并非来自编码过程中的错误,从小项目到大项目都基本上证明了这一点。因其软件缺陷很可能是在系统详细设计阶段、概要设计阶段,甚至是在需求分析阶段就存在的问题所导致的,即使针对源程序进行测试所发现的故障的根源也可能存在于软件开发前期的各个阶段。大量的事实表明,导致软件缺陷的最大原因源于软件产品的设计文档(各种设计、规划文件及说明书)。在大多数情况下,软件产品设计文档没有明确、不清楚或者描述不全面,或在软件开发过程中对需求、产品功能经常更改,或开发小组的人员之间没有完全进行很好地交流与沟通,没有很好地组织开发与执行测试流程。因此,制定软件产品开发计划非常重要,若产品计划没有制定好,软件缺陷就会潜伏在程序中,软件运行时出现问题在所难免。
软件缺陷产生的第二大来源是设计方案,这是实施软件计划的关键环节。
典型的软件缺陷产生的原因大致被归纳为以下几种类型:
(1)软件需求解释存在有错误或不明确。
(2)用户需求的定义中存在错误。
(3)软件需求中记录存在错误。
(4)软件的设计说明中有错误。
(5)软件的编码中说明有错误。
(6)软件程序代码存在错误。
(7)数据输入有错误。
(8)软件测试过程中有错误。
(9)软件问题修改得不正确或不彻底。
(10)有时不正确的结果是由于其他的软件缺陷而引起或产生的。
如图1.1所示为软件缺陷产生的原因分布图,软件产品设计的文档说明(如需求等)成为软件缺陷产生的原因的主要成分。
图1.1 软件缺陷产生的原因分布
1.1.3 软件测试的发展
通常被称为bug的软件缺陷是伴随着软件出现的,而软件测试同样也是伴随着软件的出现而出现的,随着软件规模的剧增和复杂程度的不断提升,其bug日益增多且破坏性增强,造成了严重的质量事故,使得软件开发者和使用者“对抗”软件缺陷的态度日益坚决,不断对软件质量提出新要求。由此,随着软件的诞生和发展而产生了软件测试,并越来越得到重视,同时带动了软件测试自身发展,表现在理论方面的不断丰富和技术运用及工程实践方面的日益成熟。
软件测试从软件20世纪60年代被正式建立。1961年,一个简单的软件错误导致了美国大力神州际导弹的助推器的毁灭,致使美国空军强制要求在其后的关键发射任务中,必须进行独立的验证,从而建立了软件的验证和确认的方法论,软件测试就此开始兴起。
随着软件的发展,软件测试也随之不断发展,软件测试大致经历了如图1.2所示的几个重要阶段。
图1.2 软件测试发展历程
1.软件调试
早期软件的规模较小,复杂程度相对较低,因此软件验证及错误的排查在开发阶段由开发人员在调试时就发现并加以解决,这个阶段的软件测试工作基本等同于调试工作。软件开发人员对自己的程序进行简单的测试,该阶段处于软件测试的原始阶段。而现在,大部分软件开发平台(工具)都集成调试工具,调试已成为软件开发不可或缺的一部分工作。但软件调试一般并不能解决软件的逻辑正确性和软件的功能、性能问题。
2.独立的软件测试
20世纪60年代后,软件开发者开始意识到仅靠软件调试是不够的,并不能完全找到软件的故障或缺陷,必须建立一个独立的组织进行软件测试。这个阶段的测试绝大部分是在软件产品(系统)完成之后进行的,因此测试的力度和时间有限,软件交付使用后依然有可能存在大量问题。
这个阶段还未形成任何软件测试的方法理论,主要是靠对软件错误的猜测和经验进行推断,因此并没有对软件测试进行定义,也没有对软件测试的真正含义进行深入思考。
3.软件测试首次定义
1973年,Bill Hetzel给出了软件测试的第一个定义:“软件测试就是对程序能够按预期的要求运行建立起的一种信心”。1983年,他又修改了这个定义,即“软件测试就是评价一个程序或系统的品质或能力目的的一项活动”。
这个阶段对软件测试形成的认识就是:软件测试用于验证软件(产品)是否能正确工作,并且符合要求。
但同一时期,Glenford J.Myers则将软件测试认为,软件测试不应该专注于验证软件产品是能工作的,而应该将验证软件是不工作的作为测试重点,他提出的软件测试定义是:“测试是以发现错误为目的而运行的程序或系统的执行过程”。
4.软件测试成为专门学科
20世纪80年代后,软件产业迅速发展,软件规模越来越大,复杂程度越来越高,如操作系统、大型商务软件、航天飞船控制系统、工业控制流程系统等,其软件开发人员上千人,程序几十、甚至上千万行。软件的质量受到高度重视,软件测试理论和技术得到快速发展,人们将软件测试作为重要手段来控制和保障、评价软件的质量。
1982年在美国北卡罗来纳大学召开了首次软件测试正式会议,软件测试理论开始发展,出现了多种软件测试的方法和技术。
1983年,IEEE组织对软件测试做出如下定义:
(1)使用人工或自动的手段来运行或测量软件系统的过程,目的是检验软件系统是否满足规定的要求,并找出与预期结果之间的差异。
(2)软件测试是一门需要经过设计、开发和维护等完整阶段的软件工程。
从这个阶段开始,软件测试进入新时期,成为一个专门的学科,形成了测试理论、方法和各种测试技术,并开始开发和运用某些测试工具,同时软件测试被列入软件工程的范畴,运用于软件工程实践。
5.开发与测试的融合
20世纪90年代后,软件工程发展迅速,形成了各种各样的软件开发模式,同时关于软件质量的研究和实践技术不断理论化和工程化,软件开发得到规范性的要求和约束,软件开发模式的多样化也使软件测试相辅相成,软件开发与测试出现了融合。以敏捷开发模式为代表的新一代软件开发模式在国际一流软件企业开始探索和实施,融入了软件产品开发的新思想、新模式,如极限编程、测试驱动、角色互换、团队模式,等等,并赢得很多软件开发团队的青睐,获得了成功,如应用在IBM和微软公司的多个大型软件项目开发中。
由此带来的是对软件测试的重新思考。软件测试与软件开发由相对的独立特性逐渐开始进行融合,开发人员将承担起软件测试的责任,测试人员将更多地参与到测试代码的开发中去,软件的开发与测试界限变得模糊起来。如TDD就把测试作为起点和首要任务。(Test-Driven Development,TDD,测试驱动开发。敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定了需要编写什么产品代码。TDD基本思路就是通过测试来推动整个开发的进行,但测试驱动开发并不只是单纯的测试工作,而是把需求分析、设计、质量控制量化的过程。)
6.软件测试的发展趋势
1)软件测试领域的变化
软件测试从建立到现在,经过了20多年的发展,得到了长足的进步,但与软件开发技术相比,仍处于落后的状态,发展速度更赶不上软件开发技术的前进步伐,特别是在软件产业相对落后的国家和地区。
近20年来,软件开发得益于计算机硬件的迅速发展、计算机运算速度的提高、开发语言进展、编译器及工具平台的发展等因素,因此相比早期软件的开发,无论开发速度、还是开发效率都有很大的提高。软件开发从早期的机器语言编码方式、汇编语言编码方式,到跨越了结构化编程语言,进入到面向对象程序设计的时代,开发人员的编程能力和调试速度双重受益。
对于软件测试,虽然测试工具不断涌现,自动化测试运用程度在不断提高,但并没有出现革命性的变革。软件测试相当一部分工作仍然需要依赖手工测试。软件测试方法和理论基本还在沿用20世纪的研究成果。最近十几年来,软件测试技术得到了快速发展,主要表现在:出现了众多新的软件测试方法和测试工具;软件测试工具的自动化程度更高;软件测试的组织结构、测试策略与测试技术将发生一些变化。
著名软件专家Harry Robinson在2004年预测和认为,软件测试领域将会发生下列一些变化:
(1)需求工程师、开发工程师将会成为软件测试团队成员,他们与测试人员互相帮助。
(2)测试方法日臻完善,bug预防和早期检查将会成为测试工具的主流。
(3)通过仿真工具模拟正式环境进行测试。
(4)测试用例更新变得容易。
(5)自动化测试由机器将替代人做更多工作。
(6)测试执行和测试开发的界限将变得模糊。
(7)对测试质量的衡量将从计算缺陷数量、测试用例数量转到需求的覆盖、代码的覆盖方面。
这个预见,在2009年已经部分实现了。例如,软件模型的研究取得了重大突破,基于模型的软件测试工具应运而生。
2)基于模型的软件测试技术
基于模型的软件测试技术是针对软件中的一些常见的软件模型而提出的一种测试技术。
(1)软件模型分类。
①故障模型:会引起错误的常见软件模型。
②安全漏洞模型:为他人攻击软件提供可能。
③差性能模型:软件动态运行时效率比较低下。
④并发故障模型:多线程编码。
⑤不良习惯模型:由于编码的不好习惯造成的一些错误。
⑥代码国际化模型:存在于语言进行国际化的过程中。
⑦诱骗代码模型:容易引起歧义的、迷惑人的编写方式。
基于模型的测试机理:首先提出软件模型,然后通过检测算法进行检测,如果检测算法结果是符合质量要求的,则能够从软件中排除该类模型。基于模型的软件测试工具被研制出来从而可以自动地检测软件中的故障,并且在对一些大型商业软件和开源软件的测试中发现了大量的以前测试没有发现的软件故障和安全隐患。
(2)基于模型的软件测试技术的优点。
①工具自动化程度高以及测试效率高,检测所需时间较短。
②基于模型的软件测试技术往往能发现其他测试技术难以发现的故障。
(3)基于模型的软件测试技术的缺点。
①误报问题。通常基于模型的软件测试技术都属于静态分析技术,由于某些故障的确定需要动态的执行信息,因此对于基于静态分析的工具来说,误报问题是不可避免的。
②漏报问题。漏报问题主要是由模型定义和模型检测算法引起的。目前软件模式没有一个规范的、统一的和形式化的定义。
③模型多样性。由于编程过程中,程序员具有较强的个体性,因此软件模型是多种多样的。
预计在软件测试模型、测试方法和测试服务模式方面将可能是软件测试方法研究与发展的主要内容和方向。