测试策略Word下载.docx
- 文档编号:18524946
- 上传时间:2022-12-19
- 格式:DOCX
- 页数:20
- 大小:44.09KB
测试策略Word下载.docx
《测试策略Word下载.docx》由会员分享,可在线阅读,更多相关《测试策略Word下载.docx(20页珍藏版)》请在冰豆网上搜索。
17.1.1验证和确认
软件测试是我们通常所讲的一个更为广泛的话题验证和确认(VerificationandValidation,V&V)的一个部分。
验证指的是保证软件正确地实现了某一特定功能的一系列活动。
确认指的则是保证软件的实现满足了用户需求的一系列活动。
Boehm[BOE81]是用另外一种方法来解释这两者的区别的:
验证:
“我们是否正确地完成了产品?
”
确认:
“我们是否完成了正确的产品?
V&V的定义还包含了许多我们称作软件质量保证(SQA)的许多活动。
回忆一下我们在第8章中对软件质量的讨论。
为了获取软件质量而必需的活动可以看作是图17-1中所描绘的一些组成部分。
软件工程方法提供了质量的基础,分析、设计和构造(编码)方法通过提供一致的技术和可预测的结果而帮助提高质量,正式的技术复审(跟踪检查)有助于保证作为每一个软件工程步骤的结果而产生的工作产品的质量。
在这些过程当中,测度和控制被应用于软件配置的每一个元素中。
标准和规程也有助于保证一致性,而一个形式化的SQA过程保证了“整套质量思想”的实现。
测试是质量可以被评估——更实际点说,错误可以被发现——的最后堡垒,但是,测试不应当被视为一个安全网。
象人们所说的那样,“你不能测试质量。
如果你开始测试的时候它不在那里,那么当你完成测试的时候它仍然不会在那里”。
质量在软件的整个过程中都和软件结合在一起。
方法和工具的正确使用,有效的正式技术复审和可靠的管理与测度都可以导致在测试过程中得以认可的质量。
Miller[MIL77]把软件测试和质量保证联系在一起:
“程序测试的内在动机是使用对大规模系统和小规模系统都能节约地并且有效地应用的方法来认可软件的质量。
需要重点加以注意的是,验证和确认包含了范围很广的SQA活动,其中包括正式技术复审、质量和配置审查、性能监控、仿真、可行性研究、文档复审、数据库复审、算法分析、开发测试、质量测试和安装测试[WAL89]。
虽然测试在V&V中发挥着非常重要的作用,但是其他的活动也是必要的。
17.1.2软件测试的组织
对每一个软件项目来说,在测试开始的时候总会产生一些固有的利益冲突。
开发软件的人们现在开始被要求对软件进行测试。
这本身来说似乎是无害的:
毕竟,谁能比开发人员更了解这个软件呢?
不幸的是,这些开发人员有很高的兴趣要急于证明他们的程序是毫无错误的,是按照用户的需求开发的,而且完全能够按照预定的进度和预算完成。
这些兴趣和认真地测试是相互冲突的。
从心理学的角度上来讲,软件分析和设计(包括编码)是建设性的工作。
软件工程师构造一个计算机程序、程序文档、还有相关的数据结构。
和其他任何建设者们一样,软件工程师也对自己的“大厦”感到非常骄傲,而对任何试图摧毁其“大厦”的人嗤之以鼻。
当测试开始的时候,就会存在一种微妙的、但确实存在着的、试图要“摧毁”软件工程师建立起来的东西的企图。
从开发者的观点来看,测试可以被看作是(从心理上来说)破坏性的。
所以开发者只是简单地设计和进行能够证明程序正确性的测试,而不是去尽量发现错误。
不幸的是,错误是确确实实地存在着的,而且如果软件工程师不能找到错误,那么客户就会找到它们。
通过上面的这些讨论,常常会导致人们产生如下的误解:
(1)软件的开发人员根本不应当参与测试;
(2)软件应当给那些会无情地挑毛病的陌生人来测试;
(3)测试者只有在测试的步骤即将开始的时候才参与项目。
这些想法都是错误的。
软件开发者总是负责程序的单个单元(模块)的测试,保证每个单元能够完成设计的功能。
在很多情况下,开发者也进行集成测试——进行完整的程序结构构造(和测试)的步骤。
仅仅在软件体系结构完成后,独立测试组织才开始介入。
独立测试组织(ITG)的功能就是为了避免让开发者来进行测试时会引发固有问题。
独立地测试可以消除可能存在的利益冲突。
毕竟,独立组织中的人员是靠找错误来拿工资的。
然而,软件开发人员并不能把程序交给ITG就一走了之,开发人员和ITG在软件项目中应当紧密合作,以保证测试顺利进行。
而且在测试进行过程中,那么开发人员必须可以去修改测试过程中发现的错误。
ITG从需求说明过程开始,参与了一个大项目的整个过程(计划和确定测试过程),从这种意义上来说,它是软件项目组中的一部分。
然而,在许多情况下,ITG是直接向软件质量保证组织负责的,这样它就获得它作为软件开发组织的一部分而可能得不到的独立性。
17.1.3一种软件测试策略
软件工程的过程可以看作是如图17-2所示的一个螺旋结构。
最初,系统工程定义了软件的功能,从而引出了软件需求分析,建立了软件的信息域、功能、行为、性能、约束和确认标准。
沿着螺旋向内前进,经过设计阶段,最终到达了编码。
为了开发计算机软件,我们沿着流线的螺旋前进,每一圈都会降低软件的抽象层次。
软件测试策略也可以放在螺旋的语境里来考虑(图17-2)。
单元测试从螺旋的漩涡中心开始,它着重于软件以源代码形式实现的各个单元;
测试沿着螺旋向外前进就到了集成测试,这时的测试则着重于对软件的体系结构的设计和构造;
再沿着螺旋向外走一圈,我们就遇到了确认测试,我们要用根据软件需求分析得到的需求对已经建造好的系统进行验证;
最后,我们要进行系统测试,也就是把软件和其他的系统元素放在一起进行测试。
为了对计算机软件进行测试,我们沿着螺旋的流线向外,每转一圈都扩宽了测试的范围。
从过程的观点来考虑测试的整个过程的话,在软件工程环境中的测试事实上是顺序实现的四个步骤的序列,这些步骤表示在图17-3中。
最开始,测试着重于每一个单独的模块,以确保每个模块都能正确执行,所以,我们把它叫做单元测试,单元测试大量地使用白盒测试技术,检查每一个控制结构的分支以确保完全覆盖和最大可能的错误检查;
接下来,模块必须装配或集成在一起形式完整的软件包,集成测试解决的是验证与程序构造的双重问题,在集成过程中使用最多的是黑盒测试用例设计技术,当然,为了保证覆盖一些大的分支,也会用一定数量的白盒测试技术;
在软件集成(构造)完成之后,一系列高级测试就开始了,确认标准(在需求分析阶段就已经确定了的)必须进行测试,确认测试提供了对软件符合所有功能的、行为的和性能的需求的最后保证,在确认过程中,只使用黑盒测试技术。
最后的高级测试步骤已经跳出了软件工程的边界,而属于范围更广的计算机系统工程的一部分,软件,一旦经过验证之后,就必须和其他的系统元素(比如硬件、人员、数据库)结合在一起。
系统测试要验证所有的元素能正常地啮合在一起,从而完成整个系统的功能/性能。
17.1.4测试完成的标准
每当讨论软件测试的时候都会引发一个经典问题的讨论:
“我们什么时候做测试呢?
我们又怎样才能知道我们的测试已经足够了呢?
”很遗憾的是,到目前为止对这个问题还没有一个确定性的答案,但是还是有一些在经验引导下的实际的答案和早期的尝试。
对上面问题的一个答复是:
“你永远也不可能完成测试,这个重担将会简单地从你(或者开发人员)身上转移到你的客户身上”。
每次客户/用户执行一个计算机程序的时候,程序就是在一个新的数据集合下经受测试的考验,这个清醒的事实使得其他的软件质量保证活动更加重要。
对上面问题的另一个答复是(有些讽刺,但无疑是准确的):
“当你时间不够或者资金不够用的时候,就完成了测试。
虽然很少有人会对这些答复产生异议,但是作为一个软件工程师,需要有更严格的标准来决定是否已经进行了足够的测试。
Musa和Ackerman[MUS89]提出了一个基于统计标准的答复:
“不,我们不能绝对地认定软件永远也不会再出错,但是相对于一个理论上合理的和在试验中有效的统计模型来说,如果一个在按照概率的方法定义的环境中,1000个CPU小时内不出错的操作概率大于0.995的话,那么我们就有95%的信心说我们已经进行了足够的测试。
使用概率论模型和软件可靠性理论,可以建立一种作为执行时间的函数软件故障(在测试过程中发现的错误)模型[MUS89]。
一个称为对数泊松执行时间模型(logarithmicPoissonexecution-timemodel)的软件故障模型为:
f(t)=(1/p)1n(l0pt+1)
(17.1)
其中f(t)=软件在一定的测试时间t后,可能会发生故障的预期累计数目。
l0=在测试刚开始时的初始软件故障密度(单位时间内的故障数)。
p=错误被发现和修正的过程中故障密度的指数递减值。
瞬时的故障密度,l(t)可以使用f(t)的导数得出,
l(t)=l0/(l0pt+1)
(17.2)
使用等式(17.2)中给出的关系,测试人员可以预测测试进程中错误的急剧减少。
实际的错误密度可以画在预测曲线上(图17-4)。
如果在测试过程中实际收集的数据和对数泊松执行时间模型能够在大量数据下都相当好地接近的话,那么这个模型就可以用来预测为了达到一个可以接收的低故障密度,整个测试过程所需要的时间。
通过在软件测试过程中收集数据和利用现有的软件可靠性模型,就可能得到回答“测试什么时候完成”这种问题的有意义的指导原则。
毫无疑问的是,在测试的量化规则建立之前仍然需要大量的进一步工作,但是,现有经验理论总比直觉要好不少。
17.2策略问题
在本章的后面部分,我们会探讨一个软件测试的系统化策略。
但是,如果无视一些重要的问题的话,那么即使是最好的策略也会失败。
TomGilb[GIL95]指出如果要实现一个成功的软件测试策略的话,下面的问题是必须涉及的:
在着手开始测试之前较长时间内,就要以量化的形式确定产品的需求。
虽然测试的主要目的是找错误,一个好的测试策略同样可以评估其他的质量特性,比如可移植性、可维护性和可用性(第18章)。
这些应当用一种可以测度的方式来表示,从而保证测试结果是不含糊的。
明显地指出测试目标。
测试的特定目标应当用可以测度的术语来描述。
比如,测试有效性、测试覆盖率、故障出现的平均时间、发现和改正缺陷的开销、允许剩余的缺陷密度或出现频率、以及每次回归测试的工作时间都应当在测试计划中清楚地说明[GIL95]。
了解软件的用户并为每一类用户建立相应档案通过着重于测试产品的实际用途。
“使用实例”——描述每一类用户的交互情况图(第20章)研究可以减少整个测试的工作量。
建立一个强调“快速循环测试”的测试计划。
Gilb[GIL95]建议软件工程队伍“学会以对客户有用的功能添加或/和质量改进的快速循环测试方法(用百分之二的项目开销)进行测试”。
从这些快速循环测试中得到的反馈可以用来控制质量的级别和相应的测试策略。
设计一个能够测试自身是否“强壮”的软件。
软件可以使用反调试技术(第17.3.1节)的方法来设计,这就是说,软件应当能够诊断特定类型的错误,另外,设计应当能够包括自动测试和回归测试。
使用有效的正式技术复审作为测试之前的过滤器。
正式技术复审(第8章)在发现错误方面可以和测试一样有效,由于这个原因,复审可以减少为了得到高质量软件所需的测试工作量。
使用正式技术复审来评估测试策略和测试用例本身。
正式的技术复审可以发现在测试过程中的不一致性、遗漏和完全的错误。
这样可以节省时间,同时也能够提高产品的质量。
为测试过程建立一种连续改善的实现方法。
测试策略必须进行测量,在测试过程中收集的度量数据应当被用作软件测试的统计过程控制方法的一部分。
17.3单元测试
单元测试完成对最小的软件设计单元——模块的验证工作。
使用过程设计描述作为指南,对重要的控制路径进行测试以发现模块内的错误。
测试的相关复杂度和发现的错误是由单元测试的约束范围来限定的。
单元测试通常情况下是面向白盒的,而且这个步骤可以针对多个模块并行进行。
17.3.1单元测试考虑
作为单元测试的一部分而出现的测试在图17-5中用图形的方式说明。
对模块接口的测试保证在测试时进出程序单元的数据流是正确的,对局部数据结构的检查保证临时存储的数据在算法执行的整个过程中都能维持其完整性,对边界条件的测试保证模块在极限或严格的情形下仍然能够正确执行,在控制结构中的所有独立路径(基本路径)都要走遍,以保证在一个模块中的所有语句都能执行至少一次,最后,要对所有处理错误的路径进行测试。
对穿越模块接口的数据流的测试需要在任何其他测试开始之前进行,如果数据不能正确地输入和输出的话,所有的其他测试都是没有实际意义的,Myers[MYE79]在他关于软件测试的文章中提出了接口测试的一个清单:
1.输入的形参数目是否等于实参的数目?
2.实参和形参的属性是否匹配?
3.实参和形参的单元系统是否匹配?
4.传递给被调用模块的实参数目是否等于形参的数目?
5.传递给被调用模块的实参属性是否等于形参的属性?
6.传递给被调用模块的实参的单元系统是否等于形参的单元系统?
7.传递给内置函数的数值属性和参数顺序是否正确?
8.任何对参数的引用和当前入口点是否有关联?
9.只输入的参数是否被改变了?
10.跨模块的全局变量定义是否一致?
11.约束条件是否作为参数传递?
当一个模块执行外部I/O操作的时候,必须进行附加的接口测试,下面的列表仍来自Myers[MYE79]:
1.文件属性是否正确?
2.OPEN/CLOSE语句是否正确?
3.格式规约是否和I/O语句匹配?
4.缓冲区大小是否和记录大小匹配?
5.文件是否在打开之前被使用?
6.是否处理了文件结束条件?
7.是否处理了I/O错误?
8.在输出信息里是否有文本错误?
模块的局部数据结构是经常出现的错误源。
应当设计测试用例以发现下列类型的错误:
1.不正确或者不一致的类型描述。
2.错误的初始化或缺省值。
3.不正确的(拼写错误的或被截断的)变量名字。
4.不一致的数据类型。
5.下溢、上溢和地址错误。
除了局部数据结构,全局数据对模块的影响在单元测试过程中也应当进行审查。
在单元测试过程中,对执行路径的选择性测试是最主要的任务。
测试用例应当能够发现由于错误计算、不正确的比较、或者不正常的控制流而产生的错误。
基本路径和循环测试是发现更多的路径错误的一种有效技术。
其他常见的错误有:
(1)误解的或者不正确的算术优先级;
(2)混合模式的操作;
(3)不正确的初始化;
(4)精度不够精确;
(5)表达式的不正确符号表示。
比较和控制流是紧密地耦合在一起的(比如,控制流的转移是在比较之后发生的),测试用例应当能够发现下列错误:
(1)不同数据类型的比较;
(2)不正确的逻辑操作或优先级;
(3)应该相等的地方由于精度的错误而不能相等;
(4)不正确的比较或者变量;
(5)不正常的或者不存在的循环中止;
(6)当遇到分支循环的时候不能退出;
(7)不适当地修改循环变量。
好的设计要求错误条件是可以预料的,而且当错误真的发生的时候,错误处理路径被建立,以重定向或者干净地终止处理。
Yourdon[YOU75]把这种方法叫做反调试(antidebugging),不幸的是,存在一种把错误处理过程加到软件中去,但从不进行测试的倾向。
这里有一个现实生活中的故事可以说明这个问题:
一个交互式设计系统按照合同进行开发。
在一个事务处理模块中,开发人员将错误处理信息“错误!
你不可能到达这里!
”加入到调用各种控制流分支的一系列条件测试之后。
这个“错误信息”在用户培训过程中被一个客户发现了!
在错误处理部分应当考虑的潜在错误有下列情况:
1.对错误描述的莫名其妙。
2.所报的错误与真正遇到的错误不一致。
3.错误条件在错误处理之前就引起了系统异常。
4.例外条件处理不正确。
5.错误描述没有提供足够的信息来帮助确定错误发生的位置。
边界测试是单元测试的最后(而且可能也是最为重要的)一个步骤。
软件通常是在边界情况下出现故障的,这就是说,错误往往出现在一个n元数组的第n个元素被处理的时候,或者一个i次循环的第i次调用,或者当允许的最大或最小数值出现的时候。
使用刚好小于、等于和刚好大于最大值和最小值的数据结构、控制流、数值来作为测试用例就很有可能发现错误。
17.3.2单元测试规程
单元测试通常看成为是编码步骤的附属品。
在源代码级的代码被开发、复审、和语法正确性验证之后,单元测试用例设计就开始了。
对设计信息的复审可能能够为建立前面讨论过的每一类错误的测试用例提供指导,每一个测试用例都应当和一系列的预期结果联系在一起。
因为一个模块本身不是一个单独的程序,所以必须为每个单元测试开发驱动器或/和稳定桩(stub)。
单元测试的环境如图17-6所示。
在绝大多数应用中,一个驱动只是一个接收测试数据,并把数据传送给(要测试的)模块,然后打印相关结果的“主程序”。
毫无错误的子程序桩的功能是替代那些隶属于本模块(被调用)的模块。
一个毫无错误的子程序桩或“空子程序”可能要使用子模块的接口,才能做一些少量的数据操作,并验证打印入口处的信息,然后返回。
驱动器和稳定桩都是额外的开销,这就是说,两种都属于必须开发但又不能和最终软件一起提交的软件,如果驱动器和稳定桩很简单的话,那么额外开销相对来说是很低的。
不幸的是,许多模块使用“简单”的额外软件是不能进行足够的单元测试的。
在这些情况下,完整的测试要推迟到集成测试步骤时再完成。
当一个模块被设计为高内聚时,单元测试是很简单的。
当一个模块只表示一个函数时,测试用例的数量就会降低,而且错误也就更容易被预测和发现。
17.4集成测试
一个在软件世界里初出茅庐的年轻人可能在所有的模块都已经完成单元测试之后会问这样一个似乎很合理的问题:
“如果它们每一个都能单独工作得很好,那么你为什么要怀疑把它们放在一起就不能正常工作呢?
”当然,这个问题就在于“把它们如何放在一起?
”——接口。
数据可能在通过接口的时候丢失;
一个模块可能对另外一个模块产生无法预料的副作用;
当子函数被联到一起的时候,可能不能达到期望中的功能;
在单个模块中可以接受的不精确性在联起来之后可能会扩大到无法接受的程度;
全局数据结构可能也会存在问题——还有很多,很多。
集成测试是通过测试发现和接口有关的问题来构造程序结构的系统化技术,它的目标是把通过了单元测试的模块拿来,构造一个在设计中所描述的程序结构。
通常存在进行非增量集成的倾向,也就是说,使用一步到位的方法来构造程序。
所有的模块都预先结合在一起,整个程序作为一个整体来进行测试,然后的结果通常是混乱不堪!
会遇到许许多多的错误,错误的修正也是非常困难的,因为在整个程序的庞大区域中想要分离出一个错误是很复杂的。
一旦这些错误被修正之后,就马上会有新的错误出现,这个过程会继续下去,而且看上去似乎是个无限循环的。
增量集成是一步到位的方法的对立面。
程序先分成小的部分进行构造和测试,这个时候错误比较容易分离和修正;
接口也更容易进行彻底地测试;
而且也可以使用一种系统化的测试方法。
在下面的章节中,将要讨论许多不同的增量集成策略。
17.4.1自顶向下集成
自顶向下的集成是一种构造程序结构的增量实现方法。
模块集成的顺序是首先集成主控模块(主程序),然后按照控制层次结构向下进行集成。
隶属于(和间接隶属于)主控模块的模块按照深度优先或者广度优先的方式集成到整个结构中去。
如图17-7所示,深度优先的集成首先集成在结构中的一个主控路径下的所有模块。
主控路径的选择是有些任意的,它依赖于应用程序的特性,例如,选择最左边的路径,模块M1,M2,和M5,将会首先进行集成,然后是M8或者M6(如果对M2的适当的功能是必要的),然后,开始构造中间的和右边的控制路径。
广度优先的集成首先沿着水平的方向,把每一层中所有直接隶属于上一层模块的模块集成起来,从图中来说,模块M2,M3和M4首先进行集成,然后是下一层的M5,M6,然后继续。
集成的整个过程由下列五个步骤来完成:
1.主控模块作为测试驱动器,所有的稳定桩替换为直接隶属于主控模块的模块。
2.根据集成的实现方法(如深度或广度优先),下层的稳定桩一次一个地被替换为真正的模块。
3.在每一个模块集成的时候都要进行测试。
4.在完成了每一次测试之后,又一个稳定桩被用真正的模块替换。
5.可以用回归测试(第17.4.3节)来保证没有引进新的错误。
整个过程回到第2步循环继续进行,直至这个系统结构被构造完成。
自顶向下的集成策略在测试过程的早期主要验证控制和决策点。
在一个好的程序结构中,决策的确定往往发生在层次结构中的高层,因此首先会被遇到。
如果主控制的确存在问题,尽早地发现它是很重要的。
如果选择了深度优先集成,软件的某个完整的功能会被实现和证明,例如,考虑一个经典的事务性结构(第14章),在这个结构中,有一系列复杂的交互式输入要通过一条输入路径请求、获得和验证,这条输入路径就可以用自顶向下的方式来进行集成。
早期的对功能性的验证对开发人员和客户来说都是会增加信心的。
自顶向下的策略似乎相对来说不是很复杂,但是在实践过程中,可能会出现逻辑上的问题。
最普通的这类问题出现在当高层测试需要首先对较低层次的足够测试后才能完成的时候。
在自顶向下的测试开始的时候,稳定桩代替了低层的模块,因此,在程序结构中就不会有重要的数据向上传递,测试者只有下面的三种选择:
(1)把测试推迟到稳定桩被换成实际的模块之后再进行,
(2)开发能够实现有限功能的用来模拟实际模块的稳定桩,或者(3)从层次结构的最底部向上来对软件进行集成。
图17-8给出了典型的几种稳定桩类型,从最简单的(稳定桩A)到最复杂的(稳定桩D)。
第一种实现方法(把测试推迟到稳定桩被换成实际的模块之后再进行)使我们失去了对许多在特定测试和特定模块组合之间的对应性的控制,这样可能导致在确定错误发生原
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 测试 策略