目录
一、 面向对象影响测试
1. 封装性影响测试
2. 继承性影响测试
3. 多态性影响测试
二、 面向对象测试模型
三、 面向对象分析测试
1. 对象测试
2. 结构测试
3. 主题测试
4. 属性和实例关联测试
5. 服务和消息关联测试
四、面向对象设计测试
1. 对认定类测试
2. 对类层次结构测试
3. 对类库支持测试
五、面向对象单元测试
1. 功能性和结构性测试
2. 测试用例的设计和选择
六、面向对象集成测试
七、面向对象交互测试
1. 简介
2. 交互类型
3. 交互测试
八、 面向对象的系统测试
一、 面向对象影响测试
传统的测试软件是从“小型测试”开始,逐步过渡到“大型测试”,即从单元测试开始,逐步进入集成测
试,最后进行确认测试和系统测试。对于传统的软件系统来说,单元测试集中测试最小的可编译的构件单元(模
块);单元测试结束之后,集成到系统中进行一系列的回归测试,以便发现模块接口错误和新单元加入到系统中来
所带的副作用;最后,把系统作为一个整体来测试,以发现软件需求中的错误。
面向对象的软件结构与传统的功能模块结构有所区别,类作为构成面向对象程序的基本元素,封装了数据及
作用在数据上的操作。父类定义共享的公共特征,子类除继承父类所有特征外,还引入了新的特征。
面向对象技术具有信息隐蔽、封装、继承、多态和动态绑定等特性,提高了软件开发质量,但同时也给软件
测试提出了新的问题,增加了测试的难度。
传统软件的测试往往关注模块的算法细节和模块接口间流动的数据,面向对象软件的类测试由封装在类中的
操作和类的状态行为所驱动。下面具体分析面向对象技术对软件测试的影响。
1. 封装性影响测试
类的重要特征之一是信息隐蔽,它通过对象的封装性实现。封装将一个对象的各个部分聚集在一个逻辑单元
内,对象的访问被限制在一个严格定义的接口上,信息隐蔽只让用户知道某些信息,其它信息被隐藏起来。信息
隐蔽与封装性限制了对象属性对外界的可见性与外界对它的操作权限,使得类的具体实现与它的接口相分离,降
低类和程序其它各部分之间的依赖,促进程序的模块化,避免外界对其不合理操作并防止错误的扩散。
信息隐蔽给测试带来许多问题。在面向对象软件中,对象行为是被动的,在接受到相关外部信息后才被激
活,进行相关操作返回结果。在工作过程中,对象的状态可能发生变化而进入新的状态。通过发送一系列信息创
建和激活对象,看其是否完成预期操作并处于正确状态,但是由于信息隐蔽与封装机制,类的内部属性和状态对
外界是不可见的,只能通过类自身的方法获得,这给类测试时测试用例执行是否处于预期状态的判断带来困难,
在测试时添加一些对象的实现方式和内部状态的函数考察对象的状态变化。
2. 继承性影响测试
1、反扩展性公理
反扩展性公理认为若有两个功能相同而实现不同的程序,对其中一个程序是充分的测试数据集未必对另一个
也是充分的测试数据集。这一公理表明若在子类中重定义了某一继承的方法,即使两个函数完成相同的功能,对
被继承方法是充分的测试数据集未必对重定义的方法是充分的。
2、反分解性公理
反分解性公理认为一个程序进行过充分的测试,并不表示其中的成分都得到了充分的测试。因为这些独立的
成分有可能被用在其它的环境中,此时就需要在新的环境中对这个部分重新进行测试。因此,若一个类得到了充
分的测试,当其被子类继承后,继承的方法在子类的环境中的行为特征需要重新测试。
3、反组合性公理
反组合性公理认为一个测试数据集对于程序中各个单元都是充分的并不表示它对整个程序是充分的,因为独
立部分交互时会产生在隔离状态下所不具备的新特性。这一公理表明,若对父类中某一方法进行了重定义,仅对
该方法自身或其所在的类进行重新测试是不够的,还必须测试其它有关的类(如子类和引用类)。
Perry和Kaiser对Weyuker观点总结如下:有关充分测试的直觉的结论可能是错误的。随着继承层次的加深,
虽然可供重用的类越来越多,编程效率也越来越高,但无形中加大了测试的工作量和难度。同时,递增式软件开
发过程中,如果父类发生修改,这种变化会自动传播到所有子类,使得父类和子类都必须重新测试。所以说,继
承并未简化测试问题,反而使测试更加复杂。
3. 多态性影响测试
多态使得面向对象程序对外呈现出强大的处理能力,但同时却使得程序内“同一”函数的行为复杂化,多态
促成了子类型替换。一方面,子类型替换使对象的状态难以确定。如果一个对象包含了A类型的对象变量,则A类
型的所有子类型的对象也允许赋给该变量。程序运行过程中,该变量可能引用不同类型的对象,其结构不断变
化。另一方面,子类型替换使得向父类对象发送的消息也允许向该类的子类对象发送。如果A类有两个子类B和
C,D类也有两个子类E和F,A类对象向D类对象发送消息m,则测试A类对象发出的消息m时,需考虑所有可能的
组合。
二、 面向对象测试模型
面向对象开发分为面向对象分析、面向对象设计和面向对象编程三个阶段。分析阶段产生整个问题空间的抽象描
述,设计出类和类结构,最后形成代码。面向对象测试模型能有效地将分析、设计的文本或图表代码化。测试模
型如下图所示。
三、 面向对象分析测试
1. 对象测试
OOA中认定的对象是对问题空间中实例的抽象,可从以下方面对其进行测试:
(1) 认定的对象是否全面,问题空间中所有涉及到的实例是否都反映在认定的抽象对象中。
(2) 认定的对象是否具有多个属性。只有一个属性的对象通常应看成其它对象的属性,而不是抽象为独立的对象。
(3) 对认定为同一对象的实例是否有共同的、区别于其它实例的共同属性。
(4) 对认定为同一对象的实例是否提供或需要相同的服务,如果服务随着不同的实例而变化,认定的对象就需要分
解或利用继承性来分类表示。
(5) 系统没有必要始终保持对象代表的实例信息,提供或者得到关于它的服务,认定的对象也无必要。
(6) 认定的对象的名称应该尽量准确、适用。
2. 结构测试
1、对认定的分类结构的测试
(1) 对于结构中处于高层的对象,是否在问题空间中含有不同于下一层对象的特殊可能性,即是否能派生出下一层
对象。
(2) 对于结构中处于同低层的对象,是否能抽象出在现实中有意义的更一般的上层对象。
(3) 对所有认定的对象,是否能在问题空间内抽象出在现实中有意义的对象。
(4) 高层的对象的特性是否完全体现下层的共性。
(5) 低层的对象是否具有高层对象特性基础上的特殊性。
2、对认定的组装结构的测试
(1) 整体和部件的组装关系是否符合现实的关系。
(2) 整体和部件是否在考虑的问题空间中有实际应用。
(3) 整体中是否遗漏了反映在问题空间中有用的部件。
(4) 部件是否能够在问题空间中组装新的有现实意义的整体。
3. 主题测试
主题如同文章中的内容概要,是在对象和结构基础上抽象,其提供OOA分析结果的可见性。
主题测试应该考虑以下方面:
(1) 贯彻George Miller 的“7+2”原则,如果主题个数超过7个,就要对相关密切的主题进行归并。
(2) 主题所反映的一组对象和结构是否具有相同和相近的属性及服务。
(3) 认定的主题是否是对象和结构更高层的抽象,是否便于理解OOA。
(4) 主题间的消息联系是否代表了主题所反映的对象和结构之间的所有关联。
4. 属性和实例关联测试
属性用来描述对象或结构所反映的实例特性。
实例关联用于反映实例集合间的映射关系。
对属性和实例关联的测试从如下方面考虑:
(1) 定义的属性是否对相应的对象和分类结构的每个实例都适用。
(2) 定义的属性在现实世界是否与这种实例关系密切。
(3) 定义的属性在问题空间是否与这种实例关系密切。
(4) 定义的属性是否能够不依赖于其它属性被独立理解。
(5) 定义的属性在分类结构中的位置是否恰当,低层对象的共有属性是否在上层对象属性体现。
(6) 在问题空间中每个对象的属性是否定义完整。
(7) 定义的实例关联是否符合现实。
(8) 在问题空间中实例关联是否定义完整,特别需要注意一对多和多对多的实例关联。
5. 服务和消息关联测试
服务定义了每种对象和结构在问题空间所要求的行为。
问题空间中实例的通信在OOA 中相应地定义为消息关联。
对定义的服务和消息关联的测试可从如下方面进行:
(1) 对象和结构在问题空间的不同状态是否定义了相应的服务。
(2) 对象或结构所需要的服务是否都定义了相应的消息关联。
(3) 定义的消息关联所指引的服务提供是否正确。
(4) 沿着消息关联执行的线程是否合理,是否符合现实过程。
(5) 定义的服务是否重复,是否定义了能够得到的服务。
面向对象分析测试如下表所示。
四、面向对象设计测试
结构化设计方法采用面向作业的设计方法,把系统分解为一组作业。面向对象设计采用“造型的观点”,是
以OOA为基础归纳出类,建立类结构,实现分析结果对问题空间的抽象,设计类的服务。由此可见,OOD是
OOA的进一步细化和抽象,其界限通常难以严格区分。OOD确定类和类结构不仅是满足当前需求分析的要求,更
重要的是通过重新组合或加以适当的补充,实现功能的重用和扩增。
1. 对认定类测试
OOD认定的类是OOA中认定的对象,是对象服务和属性的抽象。
认定类应该尽量是基础类,这样便于维护和重用。
测试认定类有如下一些准则:
(1) 是否涵盖了OOA中所有认定的对象。
(2) 是否能体现OOA中定义的属性。
(3) 是否能实现OOA中定义的服务。
(4) 是否对应着一个含义明确的数据抽象。
(5) 是否尽可能少地依赖其它类。
2. 对类层次结构测试
OOD的类层次结构基于OOA的分类结构产生,体现了父类和子类之间的一般性和特殊性。
类层次结构是在解空间构造实现全部功能的结构框架。
测试包含如下方面:
(1) 类层次结构是否涵盖了所有定义的类。
(2) 是否能体现OOA中所定义的实例关联。
(3) 是否能实现OOA中所定义的消息关联。
(4) 子类是否具有父类没有的新特性。
(5) 子类间的共同特性是否完全在父类中得以体现。
3. 对类库支持测试
类库主要用于支持软件开发的重用,对类库的支持属于类层次结构的组织问题。
由于类库并不直接影响软件的开发和功能实现,因此类库的测试往往作为对高质量类层次结构的评估。
其测试点如下:
(1) 一组子类中关于某种含义相同或基本相同的操作是否有相同的接口。
(2) 类中方法功能是否较单纯,相应的代码行是否较少,一般建议不超过30行。
(3) 类的层次结构是否深度大、宽度小。
五、面向对象单元测试
1. 功能性和结构性测试
类测试有两种主要的方式:功能性测试和结构性测试。功能性测试和结构性测试分别对应传统测试的黑盒测
试和白盒测试。功能性测试以类的规格说明为基础,主要检查类是否符合规格说明的要求,包括类的规格说明和
方法的规格说明两个层次。例如,对于Stack类,检查操作是否满足LIFO规则。结构性测试从程序出发,对方法进
行测试,考虑代码是否正确,Stack类检查代码是否执行正确且至少执行过一次。
测试类的方法指对方法调用关系进行测试。测试每个方法的所有输入情况,并对这些方法之间的接口进行测
试。对类的构造函数参数以及消息序列进行选择以保证其在状态集合下正常工作。因此,对类的测试分成如下两
个层次:方法内测试和方法间测试。
1) 方法内测试
方法内测试考虑类中方法,等效于传统程序中单个过程的测试,传统测试技术(如逻辑覆盖、等价类划分、边
界值分析和错误推测等方法)仍然作为测试类中每个方法的主要手段。与传统单元测试的最大差别在于方法内测试
改变了它所在实例的状态,这就要求对隐藏的状态信息进行评估。
面向对象软件中方法的执行是通过消息驱动执行的。测试类中的方法,必须用驱动程序对被测方法通过发送
消息来驱动执行。如果被测试模块或者方法调用其它模块或方法,则需要设计一个模拟被调程序功能的存根程序
代替。驱动程序、存根程序及被测模块或方法组成一个独立的可执行单元。
2) 方法间测试
方法间测试考虑类中方法之间的相互作用,对方法进行综合测试。单独测试一个方法时,只考虑其本身执行
的情况,而没有考虑方法的协作关系。方法间测试考虑一个方法调用本对象类中的其它方法,或其它类的方法之
间的通信情况。
类的操作被封装在类中,对象之间通过发送消息启动操作,对象作为一个多入口模块,必须考虑测试方法的
不同次序组合的情况。当一个类中方法的数目较多时,次序的组合数目将非常多。对于操作的次序组合以及动作
的顺序问题,测试用例中加入了激发调用信息,检查它们是否正确运行。对于同一类中方法之间的调用,遍历类
的所有主要状态。同时,选出最可能发现属性和操作错误的情况,重点进行测试。
2. 测试用例的设计和选择
1、测试用例设计
传统软件测试用例设计从软件的各个模块算法出发,而面向对象(OO)软件测试用例着眼于操作序列,以实现
对类的说明。
OO测试用例设计对OO的五个特性(局域性、封装性、信息隐藏、继承性和抽象)进行测试。Berard提出了测
试用例的设计方法,关于设计合适的操作序列以测试类的状态,主要原则包括:
(1) 对每个测试用例应当给予特殊的标识,并且还应当与测试的类有明确的联系。
(2) 测试目的应当明确。
(3) 应当为每个测试用例开发一个测试步骤列表。
2、基于概率分布的测试用例抽样
总体是指所有可能被执行的测试用例,包括所有前置条件和所有输入值可能的组合情况。样本是基于概率分
布选择的子集,子集的使用频率越高,被选中的概率越大。
样本集合中每个样本代表一个特定的个体。例如,用例模型作为测试用例分层的基础,挑选出一个测试用例
的抽样,选择一个测试系列,并不要求一定要首先明确如何来确定测试用例的总体。构建测试用例的一个测试系
列,将类说明作为测试用例的来源,运用一种抽样方法对测试进行补充,减少测试的数目。
【例】 类的实例变量取值范围为0~359,采用相关测试方法设计测试用例。
【解答】 (1) 采用基于边界值的测试方法,取0值周围的-1、0、1三种测试和359附近的358、359、400等
测试用例。(2) 采用随机测试方法,使用随机数发生器random()。由于每个测试用例的抽样为(0~359),则设计
int(random()* 360)和int(-1*random()*360)进行随机的抽样,每个值都在该区间内,且每个值被选中的概率相
等,测试不同的值。
六、面向对象集成测试
传统面向过程的软件模块具有层次性,模块之间存在着控制关系。面向对象软件功能散布在不同类中,通过
消息传递提供服务。由于面向对象软件没有一个层次的控制结构,传统软件自顶向下和自底向上的组装策略意义
不大,构成类的各个部件之间存在直接和非直接交互,软件的控制流无法确定,采用传统的将操作组装到类中的
增值式组装常常行不通。
集成测试关注于系统的结构和类之间的相互作用,测试步骤一般分成两步,首先进行静态测试,然后进行动
态测试。静态测试主要针对程序的结构进行,检测程序结构是否符合设计要求,采用逆向工程测试工具得到类的
关系图和函数关系图,与面向对象设计规格说明比较检测程序结构和实现上是否有缺陷,是否符合需求设计。
动态测试根据功能结构图、类关系图或者实体关系图,确定不需要被重复测试的部分,通过覆盖标准减少测
试工作量。
七、面向对象交互测试
1. 简介
面向对象软件由若干对象组成,通过对象之间的相互协作实现既定功能。交互既包含对象和其组成对象之间
的消息,还包含对象和与之相关的其它对象之间的消息,是一系列参与交互的对象协作中的消息的集合。例如,
对象作为参数传递给另一对象,或者当一个对象包含另一对象的引用并将其作为这个对象状态的一部分时,对象
的交互就会发生。
2. 交互类型
1、测试汇集类
汇集类是指有些类的说明中使用对象,但是实际上从不和这些对象进行协作。编译器和开发环境的类库通常包含汇集类。例如,C++的模板库、列表、堆栈、队列和映射等管理对象。汇集类一般具有如下行为:
(1) 存放这些对象的引用;
(2) 创建这些对象的实例;
(3) 删除这些对象的实例。
2、测试协作类
凡不是汇集类的非原始类就是协作类。协作类是指在一个或多个操作中使用其它的对象并将其作为实现中不
可缺少的一部分。协作类测试的复杂性远远高于汇集类的测试,协作类测试必须在参与交互的类的环境中进行测
试,需要创建对象之间交互的环境。
3. 交互测试
系统交互既发生在类内方法之间,也发生在多个类之间。类A与类B交互如下所述:
(1) 类B的实例变量作为参数传给类A的某方法,类B的改变必然导致对类A的方法的回归测试。
(2) 类A的实例作为类B的一部分,类B对类A中变量的引用需进行回归测试。
交互测试的粒度与缺陷的定位密切相关,粒度越小越容易定位缺陷。
但是,粒度小使得测试用例数和测试执行开销增加。
因此,测试权衡于资源制约和测试粒度之间,应正确地选择交互测试的粒度。
八、 面向对象的系统测试
单元测试和集成测试仅能保证软件开发的功能得以实现,不能确认在实际运行时是否满足用户的需要,因
此,必须对软件进行规范的系统测试。确认测试和系统测试不关心类之间连接的细节,仅着眼于用户的需求,测
试软件在实际投入使用中与系统其它部分配套运行的情况,保证系统各部分在协调工作的环境下能正常工作。
系统测试参照面向对象分析模型,测试组件序列中的对象、属性和服务。组件是由若干类构建的,首先实施
接受测试。接受测试将组件放在应用环境中,检查类的说明,采用极值甚至不正确数值进行测试。其次,组件的
后续测试应顺着主类的线索进行。