购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.3 软件测试

软件测试是为了发现错误而执行程序的过程,是根据程序开发阶段的规格说明及程序内部结构而精心设计的一批测试用例(输入数据及其预期结果的集合),并利用这些测试用例去运行程序,以发现程序错误的过程。

从软件开发者的角度出发,则希望软件测试成为表明软件产品中不存在错误的过程,验证该软件已正确地实现了用户的要求,确立人们对软件质量的信心。从用户的角度出发,普遍希望通过软件测试暴露软件中隐藏的错误和缺陷,以考虑是否可接受该产品。

应当把“尽早地和不断地进行软件测试”作为软件开发者的座右铭;测试用例应当由测试输入数据和对应的预期输出结果这两部分组成;程序员应避免检查自己的程序;在设计测试用例时,应包括合理的输入条件和不合理的输入条件;充分注意测试中的群集现象。经验表明,测试后程序中残存的错误数目与该程序中已发现的错误数目成正比。严格执行测试计划,排除测试的随意性;应当对每一个测试结果做全面检查;妥善保存测试计划、测试用例、出错统计和最终分析报告,为软件维护提供方便。

软件测试并不等于程序测试。软件测试应贯穿于软件定义与开发的整个期间。需求分析、概要设计、详细设计,以及程序编码等各阶段所得到的文档,包括需求规格说明、概要设计规格说明、详细设计规格说明,以及源程序,都应成为软件测试的对象。

2.3.1 测试用例设计

测试用例是为特定目标开发的测试输入、执行条件和预期结果的集合。设计测试用例通常有两种常用的测试方法:黑盒测试和白盒测试。

1.黑盒测试

黑盒测试把测试对象看做一个空盒子,不考虑程序的内部逻辑结构和内部特性,只依据程序的需求规格说明书,检查程序的功能是否符合它的功能说明,又称为功能测试或数据驱动测试。

黑盒测试方法主要是在程序的接口上进行测试,主要是为了发现以下错误。

黑盒测试的测试用例设计方法主要有如下几种。

(1)等价类划分。 等价类划分是一种典型的黑盒测试方法,使用这一方法时,完全不考虑程序的内部结构,只依据程序的规格说明来设计测试用例。该方法把所有可能的输入数据即程序的输入域划分为若干个部分,然后从每一部分中选取少数有代表性的数据作为测试用例。

使用这一方法设计测试用例要经历划分等价类(列出等价类表)和选取测试用例两步。

(2)边界值分析。 边界值分析也是一种黑盒测试方法,是对等价类划分方法的补充。人们从长期的测试工作经验得知,大量的错误是发生在输入或输出范围的边界上,而不是在输入范围的内部。因此针对各种边界情况设计测试用例,可以查出更多的错误。使用边界值方法设计测试用例,应当选取正好等于、刚刚大于或刚刚小于边界的值作为测试数据。

(3)错误推测法。 人们也可以靠经验和直觉推测程序中可能存在的各种错误,从而有针对性地编写检查这些错误的例子。其基本思想是:列举出程序中所有可能有的错误和容易发生错误的特殊情况,根据它们选择测试用例。

(4)因果图。 如果在测试时必须考虑输入条件的各种组合,可使用一种适于描述多种条件的组合,相应产生多个动作的形式来设计测试用例,这就需要利用因果图。这种方法最终生成的就是判定表。它适合于检查程序输入条件的各种组合情况。用因果图生成测试用例的基本步骤是:

2.白盒测试

白盒测试把测试对象看做一个透明的盒子,它允许测试人员利用程序内部的逻辑结构和有关信息,设计或选择测试用例,对程序所有逻辑路径进行测试。通过在不同点检查程序的状态,确定实际的状态是否与预期的状态一致,又称为结构测试或逻辑驱动测试。

白盒测试主要对程序模块进行如下检查:

3.逻辑覆盖

逻辑覆盖是以程序内部的逻辑结构为基础的设计用例的技术。它属白盒测试,包括语句覆盖、判定覆盖、条件覆盖、判定-条件覆盖、条件组合覆盖、路径覆盖等。

2.3.2 软件测试策略

从测试实际的前后过程来看,软件测试是由一系列不同的测试所组成,这些软件测试的步骤分为:单元测试、集成测试(又称组装测试)、确认测试和系统测试。软件开发的过程是自顶向下的,测试则正好相反,以上这些过程就是自底向上,逐步集成的。

1.单元测试

单元测试也称为模块测试,是针对每个模块进行的测试,可从程序的内部结构出发设计测试用例,多个模块可以平行地对立地测试。通常在编码阶段进行,必要的时候要制作驱动模块和桩模块。

驱动模块是指在单元测试和集成测试中,协调输入和输出的测试程序;桩模块指模拟被调用单元的程序。

单元测试可以测试模块接口、局域数据结构、独立路径、错误处理路径和边界条件五个方面的内容。

2.集成测试

在单元测试的基础上,将所有模块按照设计要求组装成系统,必须精心计划,应提交集成测试计划、集成测试规格说明和集成测试分析报告。

这时需要考虑的问题是:在把各个模块连接起来的时候,穿越模块接口的数据是否会丢失;一个模块的功能是否会对另一个模块的功能产生不利的影响;各个子功能组合起来,能否达到预期要求的父功能;全局数据结构是否有问题;单个模块的误差累积起来,是否会放大,从而达到不能接受的程度。

把模块组装为系统的方式有两种:一次性组装方式和增殖式组装方式。在组装测试时,应当确定关键模块,对这些关键模块及早进行测试,这些关键模块具有如下特征:满足某些软件需求,在程序的模块结构中位于较高的层次,较复杂易发生错误,有明确定义的性能要求。

3.确认测试

确认测试验证软件的功能、性能及其他特性是否与用户的要求一致。

对软件的功能和性能要求在软件需求规格说明书中已经明确规定。它包含的信息就是软件确认测试的基础。确认测试的主要步骤如下。

首先进行有效性测试: 在模拟的环境(可能就在开发的环境)下,运用黑盒测试的方法,验证被测软件是否满足需求规格说明书列出的需求,通过制订确认测试计划和执行测试计划确定软件的功能和性能等特性是否与需求相符、所有的文档都是正确且便于使用的,同时对于其他软件需求(可移植性、兼容性、出错自动恢复、可维护性等)都要进行测试。

其次进行软件配置复查,保证做到: 软件配置的所有成分都齐全;各方面的质量均符合要求;具有维护阶段所必需的细节,而且已经编排好分类的目录。

最后进行验收测试: 验收测试是以用户为主的测试,软件开发人员和质量保证人员也应参加,由用户参加设计测试用例,使用生产中的实际数据进行测试。在测试过程中,除了考虑软件的功能和性能外,还应对软件的可移植性、兼容性、可维护性、错误的恢复功能等进行确认。

确认测试应交付的文档有:确认测试分析报告;最终的用户手册和操作手册;项目开发总结报告。

4.系统测试

系统测试是将软件放在整个计算机环境下,包括软硬件平台、某些支持软件、数据和人员等,在实际运行环境下进行一系列的测试。系统测试的目的是通过与系统的需求定义做比较,发现软件与系统的定义不符合或与之矛盾的地方。

5.α测试和β测试

在软件交付使用之后,用户将如何实际使用程序,对于开发者来说是不知道的。通常在软件发布上市之前需要进行α测试和β测试。

α测试是由一个用户在开发环境下进行的测试,也可以是公司内部的用户在模拟实际操作环境下进行的测试。α测试的目的是评价软件产品的FLURPS(功能、局域化、可使用性、可靠性、性能和支持)。尤其注重产品的界面和特色。

α测试可以从软件产品编码结束之时开始,或者在模块(子系统)测试完成之后开始,也可以在确认测试过程中产品达到一定的稳定和可靠程度之后再开始。

β测试是由软件的多个用户在实际使用环境下进行的测试。这些用户返回有关错误信息给开发者。

由于β测试时,开发者通常并不在测试现场。因而,β测试是在开发者无法控制的环境下进行的软件现场应用。在β测试中,由用户记下遇到的所有问题,包括真实的,以及主观认定的,定期向开发者报告。β测试主要衡量产品的FLURPS,着重于产品的支持性,包括文档、客户培训和支持产品生产能力。只有当α测试达到一定的可靠程度时,才能开始β测试。它处在整个测试的最后阶段。同时,产品的所有手册文本也应该在此阶段完全定稿。

2.3.3 软件测试类型

软件测试由一系列不同的测试组成。主要目的是对以计算机为基础的系统进行充分的测试。软件测试大致可以分为如下几大类。

(1)功能测试。 功能测试是在规定的一段时间内运行软件系统的所有功能,以验证这个软件系统有无严重错误。

(2)可靠性测试。 如果系统需求说明书中有对可靠性的要求,则需进行可靠性测试。

可靠性测试的评价指标主要有平均故障间隔时间MTBF和平均故障修复时间MTTR。平均失效间隔时间MTBF是否超过规定时限,因故障而停机的时间MTTR在一年中应不超过多少时间。

(3)强度测试。 强度测试是要检查在系统运行环境不正常乃至发生故障的情况下,系统可以运行到何种程度的测试。例如:把输入数据速率提高一个数量级,确定输入功能将如何响应;设计需要占用最大存储量或其他资源的测试用例进行测试;设计出在虚拟存储管理机制中引起“颠簸”的测试用例进行测试;设计出会对磁盘常驻内存的数据过度访问的测试用例进行测试。强度测试的一个变种就是敏感性测试。敏感性测试是指在程序有效数据界限内一个小范围内的一组数据可能引起极端的或不平稳的错误处理出现,或者导致极度的性能下降的情况发生。此测试用以发现可能引起这种不稳定性或不正常处理的某些数据组合。

(4)性能测试。 性能测试是要检查系统是否满足在需求说明书中规定的性能,特别是对实时系统或嵌入式系统。性能测试常常需要与强度测试结合起来进行,并常常要求同时进行硬件和软件检测。通常,对软件性能的检测表现在以下几个方面:响应时间、吞吐量、辅助存储区。例如,缓冲区、工作区的大小、数据处理精度等。

(5)恢复测试。 恢复测试是要证实在克服硬件故障(包括掉电、硬件或网络出错等)后,系统能否正常地继续进行工作,并不对系统造成任何损害。为此,可采用各种人工干预的手段,如模拟硬件故障,故意造成软件出错等。并由此检查错误探测功能:系统能否发现硬件失效与故障;能否切换或启动备用的硬件;在故障发生时能否保护正在运行的作业和系统状态;在系统恢复后能否从最后记录下来的无错误状态开始继续执行作业,等等。掉电测试:其目的是测试软件系统在发生电源中断时能否保护当时的状态且不毁坏数据,然后在电源恢复时从保留的断点处重新进行操作。

(6)启动/停止测试。 这类测试的目的是验证在机器启动及关机阶段,软件系统正确处理的能力。这类测试包括:反复启动软件系统(例如操作系统自举、网络的启动、应用程序的调用等),在尽可能多的情况下关机。

(7)配置测试。 这类测试是要检查计算机系统内各个设备或各种资源之间的相互联结和功能分配中的错误。它主要包括以下几种:配置命令测试即验证全部配置命令的可操作性(有效性),特别是对最大配置和最小配置要进行测试,软件配置和硬件配置都要测试;循环配置测试即证明对每个设备物理与逻辑的,逻辑与功能的每次循环置换都能正常工作;修复测试即检查每种配置状态及哪个设备是坏的,并用自动或手工的方式进行配置状态间的转换。

(8)安全性测试。 安全性测试是要检验在系统中已经存在的系统安全性、保密性措施是否发挥作用,有无漏洞。力图破坏系统的保护机构以进入系统的主要方法有以下几种:正面攻击或从侧面、背面攻击系统中易受损坏的那些部分;以系统输入为突破口,利用输入的容错性进行正面攻击;申请和占用过多的资源使系统崩溃,以破坏安全措施,从而进入系统;故意使系统出错,利用系统恢复的过程,窃取用户口令及其他有用的信息;通过浏览残留在计算机各种资源中的垃圾(无用信息),以获取如口令、安全码、译码关键字等信息;浏览全局数据,期望从中找到进入系统的关键字;浏览那些逻辑上不存在,但物理上还存在的各种记录和资料等。

(9)可使用性测试。 可使用性测试主要从使用的合理性和方便性等角度对软件系统进行检查,发现人为因素或使用上的问题。要保证在足够详细的程度下,用户界面便于使用;输入量可容错、响应时间和响应方式合理可行;输出信息有意义、正确并前后一致;出错信息能够引导用户去解决问题;软件文档全面、正规、确切。

(10)安装测试。 安装测试的目的不是查找软件错误,而是查找安装错误。在安装软件系统时,会有多种选择,例如,要分配和装入文件与程序库、布置适用的硬件配置,以及进行程序的联结;而安装测试就是要找出在这些安装过程中出现的错误。安装测试是在系统安装之后进行测试。它要检验:用户选择的一套任选方案是否相容,系统的每一部分是否都齐全,所有文件是否都已产生并确有所需要的内容,硬件的配置是否合理,等等。

(11)过程测试。 在一些大型的系统中,部分工作由软件自动完成,其他工作则需由各种人员,包括操作员、数据库管理员、终端用户等,按一定规程同计算机配合,靠人工来完成。指定由人工完成的过程也需要经过仔细的检查,这就是所谓的过程测试。

(12)容量测试。 容量测试是要检验系统的能力最高能达到什么程度,例如,对于编译程序,让它处理特别长的源程序;对于操作系统,让它的作业队列“满员”;对于信息检索系统,让它使用频率达到最大。在使系统的全部资源达到“满负荷”的情况下,测试系统的承受能力。

(13)文档测试。 这种文档测试是检查用户文档(如用户手册)的清晰性和精确性。

(14)兼容性测试。 这类测试主要想验证软件产品在不同版本之间的兼容性。有两类基本的兼容性测试:向下兼容和交错兼容。

2.3.4 面向对象的软件测试

面向对象的开发模型突破了传统的瀑布模型,将开发分为面向对象分析(OOA)、面向对象设计(OOD)和面向对象编程(OOP)三个阶段。分析阶段产生整个问题空间的抽象描述,在此基础上,进一步归纳出适用于面向对象编程语言的类和类结构,最后形成代码。由于面向对象的特点,采用这种开发模型能有效地将分析设计的文本或图表代码化,不断适应用户需求的变动。针对面向对象的开发模型,结合传统的测试步骤划分,提出一种面向对象的测试模型。该模型包括OOA Test、OOD Test、OOP Test、面向对象单元测试、集成测试和系统测试。

OOA Test和OOD Test分别是对分析结果和设计结果的测试,主要是对分析和设计产生的文本进行测试,是软件开发前期的关键性测试。OOP Test主要针对编程风格和程序代码实现进行测试,其主要测试内容在面向对象单元测试和面向对象集成测试中体现。面向对象单元测试是对程序内部具体单一的功能模块的测试,如果程序是用C++语言实现,主要就是对类成员函数的测试。面向对象单元测试是进行面向对象集成测试的基础。面向对象集成测试主要对系统内部的相互服务进行测试,如成员函数间的相互作用,类间的消息传递等。

1.面向对象分析的测试

传统的面向过程分析是一个功能分解的过程,把一个系统看成可以分解的功能的集合。这种传统的功能分解法的着眼点在于一个系统需要什么样的信息处理方法和过程,以过程的抽象来对待系统的需要。而面向对象分析是把E-R图和语义网络模型,即信息造型中的概念,与面向对象程序语言中的重要概念结合在一起而形成的分析方法,最后通常得到的是问题空间的图表的形式描述。

OOA直接映射问题空间,全面地将问题空间中实现功能的现实抽象化。将问题空间中的实例抽象为对象,用对象的结构反映问题空间的复杂实例和复杂关系,用属性和服务表示实例的特性和行为。对一个系统而言,与传统分析方法产生的结果相反,行为相对稳定,结构则相对不稳定,这更充分反映了现实的特性。由于OOA的结果是为后面阶段类的选定和实现,类层次结构的组织和实现提供平台。

因此,OOA对问题空间分析抽象的不完整,最终会影响软件的功能实现,导致软件开发后期大量可避免的修补工作;而一些冗余的对象或结构会影响类的选定、程序的整体结构或增加程序员不必要的工作量。由此可见,对OOA的测试重点应该放在完整性和冗余性方面。OOA阶段的测试划分为以下五个方面:对认定的对象的测试;对认定的结构的测试;对认定的主题的测试;对定义的属性和实例关联的测试;对定义的服务和消息关联的测试。

2.面向对象设计的测试

通常结构化的设计方法是用面向作业的设计方法,它把系统分解后,提出一组作业,这些作业是以过程实现系统的基础构造,把问题域的分析转化为求解域的设计,分析的结果是设计阶段的输入。

而面向对象设计(OOD)采用“造型的观点”,以OOA为基础归纳类,并建立类结构或进一步构造成类库,实现分析结果对问题空间的抽象。OOD归纳的类可以是对象简单的延续,也可以是不同对象的相同或相似的服务。因为OOD是OOA的进一步细化和更高层的抽象。所以,OOD与OOA的界限通常难以区分。由于OOD确定类和类结构不仅能满足当前需求分析的要求,更重要的是通过重新组合或加以适当的补充,能方便实现功能的重用和扩充,以不断适应用户的要求。因此,对OOD的测试,建议针对功能的实现和重用,以及对OOA结果的扩展,从如下三方面考虑:

其中,对构造的类层次结构的测试通常基于OOA中产生的分类结构的原则来组织,着重体现父类和子类间的一般性和特殊性。在当前的问题空间,对类层次结构的主要要求是能在解空间构造实现全部功能的结构框架。为此,测试如下几个方面:

3.面向对象编程的测试

典型的面向对象程序具有继承、封装和多态的新特性,这使得传统的测试策略必须有所改变。封装是对数据的隐藏,外界只能通过被提供的操作来访问或修改数据,这样降低了数据被任意修改和读写的可能性,降低了传统程序中对数据非法操作的测试。继承使传统测试遇到了一个难题,即对继承的代码究竟如何测试,多态性使得面向对象程序对外呈现出强大的处理能力,但同时却使程序内“同一”函数的行为复杂化,测试时不得不考虑不同类型具体执行的代码和产生的行为。

面向对象程序是把功能的实现封装在类中,能正确实现功能的类,通过消息传递来协同实现设计要求的功能。正是这种面向对象程序风格,将出现的错误能精确地确定在某一具体的类。在面向对象编程(OOP)阶段,将测试集中在类功能的实现和相应的面向对象程序风格,主要体现为以下两个方面:

(1)数据成员是否满足数据封装的要求;

(2)类是否实现了要求的功能。

4.面向对象的单元测试

传统的单元测试是针对程序的函数、过程或完成某一定功能的程序块。沿用单元测试的概念,实际测试类成员函数。传统的单元测试方法在面向对象的单元测试中都可以使用。

面向对象编程的特性使得对成员函数的测试,又不完全等同于传统的函数或过程测试。尤其是继承特性和多态特性,使子类继承或过载的父类成员函数出现了传统测试中未遇见的问题。需做以下的考虑。

(1)继承的成员函数是否都不需要测试。 对父类中已经测试过的成员函数,下面两种情况需要在子类中重新测试:继承的成员函数在子类中做了改动;成员函数调用了改动过的成员函数的部分。

(2)对父类的测试是否能照搬到子类。 只需在父类测试要求和测试用例上添加对子类函数的新的测试要求和增补相应的测试用例。

5.面向对象的集成测试

面向对象的集成测试通常需要在整个程序编译完成后进行。此外,面向对象程序具有动态特性,由于程序的控制流往往无法确定,因此也只能对整个编译后的程序做基于黑盒子的集成测试。

面向对象的集成测试能够检测出,相对独立的单元测试无法检测出的那些类相互作用时才会产生的错误。基于单元测试对成员函数行为正确性的保证,集成测试只关注系统的结构和内部的相互作用。面向对象的集成测试可以先进行静态测试,再进行动态测试。

静态测试主要针对程序的结构进行,检测程序结构是否符合设计要求,提供一种成为“可逆性工程”的功能,即通过原程序得到类关系图和函数功能调用关系图,将“可逆性工程”得到的结果与OOD的结果相比较,检测程序结构和实现是否有缺陷,即通过这种方法检测OOP是否达到了设计要求。

动态测试设计测试用例时,通常需要上述的功能调用结构图、类关系图或者实体关系图为参考,确定不需要被重复测试的部分,从而优化测试用例,减少测试工作量,使得进行的测试能够达到一定的覆盖标准。测试所要达到的覆盖标准可以是达到类所有的服务要求或服务提供的一定覆盖率,依据类间传递的消息,达到对所有执行线程的一定覆盖率,达到类的所有状态的一定覆盖率等。同时也可以考虑使用现有的一些测试工具来得到程序代码执行的覆盖率。值得注意的是设计测试用例时,不但要设计确认类功能满足的输入,还应该有意识地设计一些被禁止的例子,确认类是否有不合法的行为产生,如发送与类状态不相适应的消息,要求不相适应的服务等。

6.面向对象的系统测试

系统测试应该尽量搭建与用户实际使用环境相同的测试平台,保证被测系统的完整性,对临时没有的系统设备部件,也应有相应的模拟手段。系统测试时,应该参考OOA分析的结果,对应描述的对象、属性和各种服务,检测软件是否能够完全“再现”问题空间。系统测试不仅是检测软件的整体行为表现,从另一个侧面看,也是对软件开发设计的再认识。系统测试需要对被测的软件结合需求分析做仔细的测试分析,建立测试用例。 sNmQnK5gnPHtFuBsvQ6SB2MssBHMcfr55BFdj95o2K2zjMDlbPRrACOVHT7IEpBB

点击中间区域
呼出菜单
上一章
目录
下一章
×