《软件测试技术》课件第9章_第1页
《软件测试技术》课件第9章_第2页
《软件测试技术》课件第9章_第3页
《软件测试技术》课件第9章_第4页
《软件测试技术》课件第9章_第5页
已阅读5页,还剩97页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第9章面向对象软件的测试9.1面向对象技术对软件测试的影响9.2面向对象软件测试的层次9.3面向对象的单元测试9.4面向对象的集成测试9.5面向对象的系统测试9.6面向对象的测试和传统测试的比较9.7本章小结9.1面向对象技术对软件测试的影响

面向对象技术因为能够解决传统程序设计语言存在的问题,自提出以后,一度成为研究热点。事实上,采用面向对象技术减少了不少错误的发生,对于提高软件质量起到了很大的作用。

面向对象程序设计的核心思想就是对客观事物的抽象。它是从众多现实生活中感性的事物中抽取事物共同本质的特征,舍弃其非本质的特征。它是将这些本质的特性映射到现实概念类,并且模拟现实的事物,通过对类进行实体化而产生对象的过程。面向对象就是一个由具体到抽象,再由抽象到具体的过程。在这个过程中,把数据及其操作作为一个整体对待,数据本身不能被外部过程直接存取。其具体过程如图9-1-1所示。图9-1-1面向对象程序的设计示意图9.1.1封装对测试的影响

封装是类的基本特性之一。它将一个对象的各个部分聚集在一个逻辑单元内。封装将类的所有数据和操作作为一个整体对待,数据本身不能被外部过程直接存取,促进了程序的模块化,并在一定程度上简化了类的使用,避免了不合理的操作,并能有效地阻止错误的扩散。但同时,它们也给程序的测试带来了一定的问题。封装将数据、操作等集成在一个相对独立的程序单元——类中,类中方法的执行离不开一定的对象环境。测试类中的任何一个方法都必须首先将这个类实例化。因此,测试用例的设计与其他测试的不同之处就是在测试设计中对桩模块的设计有所不同。传统测试中的桩模块是模拟函数,在面向对象的测试设计中,则是通过类的实体对象来访问其属性和方法。另外,对于具体的访问与对象的状态相关。因此,在测试用例的设计过程中还需要考虑对象的状态。在面向对象的测试中,如果桩仅仅单一地模拟类中的方法,已经不适用了。在面向对象的测试中对桩设计时,桩模拟的不再是传统测试中的函数,而是模仿对象(MockObject)。例如,要单元测试一个使用HttpRequest的对象,测试时需要启动Web服务器,构造Request实例并填入所需要的值,测试完后需要停止Web服务器,这一系列过程可能很麻烦。模仿对象提供了解决这一困难的方法。模仿对象符合实际对象的接口,但需要有足够的代码来“欺骗”测试对象并跟踪其行为。

模仿对象给桩设计提出一个解决方案:MockObject拥有与被测对象的“合作者”完全一致的接口,在测试中作为“合作者”被传递给被测对象。当被测对象调用合作者时,MockObject根据测试者的意愿改变某些状态或返回期望的结果,以检查被测程序是否按照所期望的逻辑进行工作,达到测试的目的。或者说,MockObject作为“仿真器”出现在测试用例中,对被测对象进行“欺骗”和跟踪。而只要MockObject的行为与被测对象所期望的一致,就不会对被测对象产生任何影响。这种模式对于许多情况都非常有效,但模仿对象有时不能被传递到正在测试的对象。而设计该对象是为了创建、查找或获得其合作者。具体的做法以Java为例的单元测试来简单说明。首先要进行测试一个计算赢得抽奖的几率的程序,例如,如果从1到50中的数字中抽取6个数字来作为中奖号码,那么共有(50×49×48×46×45)/(1×2×3×4×5×6)个可能的中奖号码。所以中奖的几率为1/1890700。一般情况从n个数字中抽取k个数字,那么会有(n×(n-1)×(n-2)×…×(n-k))/(1×2×3×…×k)种可能。

下面通过程序来实现,在该程序中由两个类来实现,一个接收界面数据并输出结果,另一个计算几率。现在需要测试接收界面数据并输出结果的类,代码如下。下面我们需要测试类中的方法print(),在该方法中涉及到类LotteryOdds,程序通过类LotteryOdds来计算几率。那么我们需要设置桩模块。这里的桩模块就需要建立MockObject来模拟LotteryOdds。具体过程为:首先将创建代码抽取出工厂方法,由工厂方法来完成创建LotteryOdds对象,在测试子类中覆盖该工厂方法,通过该覆盖的工厂方法创建模仿对象;然后调用覆盖的工厂方法来创建对象,那么创建的就是模仿对象,而不再是原LotteryOdds对象。这样就可以完成单元测试了。具体步骤如下。

1.重构代码

重构是一种代码更改,它使原始功能保持不变,但更改了代码设计,使它变得更清晰、更有效且更易于测试。在本例中,重构的主要工作就是将产生LotteryOdds的过程独立处理成为类的方法之一。重构后的代码如下:

2.建立MockObject

建立类来模拟LotteryOdds类,可以直接继承LotteryOdds类。这里我们建立私有类,这样可以将该MockObject类直接放到测试用例类中,使得MockObject对象是针对单一的测试用例来设计,并可以进一步简化MockObject的设计。具体的实现代码如下:

3.建立TestCase,通过覆盖的方法返回模仿对象

建立测试类,在测试类中建立被测试对象时,通过方法覆盖的方式返回模拟对象。测试用对象调用的方法来模仿对象的方法。这里也利用了面向对象设计中的多态和动态绑定的技术。测试代码如下:执行测试中,被测试对象Lottery所调用的LotteryOdds对象被MockLotteryOdds代替。其调用的方法均为MockLotteryOdds所实现的方法。

通过建立MockObject的方法,可以实现对对象的模拟,在面向对象的单元测试、集成测试中都会用到该技术。在实际的应用中,遇到HttpRequest、HttpServletRequest、JDBC中的ResultSet对象时,就需要建立较为复杂的MockObject了。现在有许多功能来帮助我们建立这些复杂的MockObject,如在Java阵营中主要的Mock测试工具有JMock、MockCreator、Mockrunner、EasyMock、MockMaker等,而在微软的.Net阵营中主要是Nmock、.NetMock等。9.1.2信息隐藏对测试的影响

在封装的基础上,为了进一步保证类的数据、操作的安全性,在面向对象的设计中又引入了信息隐藏技术。信息隐藏是指只让用户知道那些确保用户正确使用一个对象所必需的信息,其他信息对用户来说则被隐藏起来。对于面向对象的设计中共有的属性和方法是可以访问的,而私有的和受保护的属性和方法,其访问却是受到限制的。这样一来,对于面向对象的测试,信息隐藏带来的主要问题则是对象状态的观察问题。由于信息隐藏机制的存在,类的内部对外界来说是“不可见”的,其属性和状态只能通过类自身的方法或函数来获得。这就给测试用例(尤其是预期结果)的生成带来了一定的困难。为了能够观察到这些属性和状态,以确定程序执行的结果是否正确,在测试时可以采用以下技术来跳过信息隐藏,获取类的属性和状态。

(1)修改被测试类,通过增加操作向测试者提供对象的属性,但是这种方法是强制的,同时也不能确定引入的操作与类中原有的操作是否重名。

(2)在一个继承类中定义新操作,该类继承于测试类,并且只是用于协助测试,这个操作将获取测试类的属性。如果类的某些属性不能为子类所访问也是无用的,例如Java语言的私有属性。

(3)某些语言通过引入一些机制来打破封装,例如C++语言的friendsmembers。在Java中可以运用其反射机制来实现。如测试中也可以通过getDeclaredMethods()来获取所有的方法,包括私有方法。选择解决方法的基本原则就是采用尽量不改变被测试类且能够获取属性的最简单的方法。方法(1)的缺点在于需要修改被测试类,方法(2)的缺点在于所要编写的代码量较多,方法(3)较可取。下面我们以Java程序为例,来说明方法(3)的用法。

在本例中,要测试对私有方法和私有属性的访问。下面是要测试的代码类Unit,在示例代码中,有两个私有方法,一个是带参数的,另外一个是不带参数的。另外包含一个属性,并建立set和get方法来访问类的属性。在设计测试用例时,为了获取类中的方法并运行它,可以通过Java获取类方法后,通过反射机制来访问。在测试用例的设计中,重点用到getDeclaredMethods()方法来获取类的所有方法,用getDeclaredFields()来获取类的所有属性,这包括公有、私有及包含成员。可以通过反射机制调用invoke()来调用对象的方法。对于属性而言,还是不能直接访问私有属性,而需要通过set和get方法来访问。具体的测试用例如下。下面的测试用例中,对私有方法getData()和getData(finalinti)进行了测试,还对私有属性myField的状态变化进行了相应的测试。

importjava.lang.reflect.Field;

importjava.lang.reflect.Method;

importorg.junit.After;

importorg.junit.Before;

importorg.junit.Test;

importjunit.framework.Assert;

publicclassHideInforTest{

privateHideInforc;

privatefinalMethodmethods[]=HideInfor.class.getDeclaredMethods();

privatefinalFieldfield[]=HideInfor.class.getDeclaredFields();

@Before

publicvoidsetUp()throwsException{

c=newHideInfor(10);

c.setMyField(20);

}

@Test

publicvoidtestGetData()throwsException{

for(inti=0;i<methods.length;++i){

if(methods[i].getName().equals("getData")){}在本例中,使用了JUnit,当然如果使用JUnit扩展包JUnitX,可以不必担心这些问题。JUnitX提供测试私有和保护方法的单元测试工具,它建立在JUnit之上,其关键类JUnitx.framework.PrivateUseCase继承了JUnit.framework.TestCase,所以JUnit提供的TestRunner可以直接运行基于PrivateUseCase的测试类。9.1.3继承对测试的影响

继承是面向对象设计中的另一重要特性。继承性表达了类与类之间的关系,一个类可以定义为另一个类的扩充或受限。继承性是自动地共享类、子类和对象中的方法和数据的机制。每个对象都是某个类的实例,一个系统中类对象是各自封闭的。如果没有继承机制,则类、对象中的数据和方法就可能出现大量重复。继承使用已有的定义作为建立新定义的基础,它由三种基本机制(扩展、覆盖、特例化)来实现。扩展是子类自动包含超类的特征;覆盖是子类中的方法与超类中的方法有相同的名字和消息参数,接口相同,但方法的实现通常不同;特例化是子类中特有的方法和实例变量。继承又可分为单继承和多继承等。在具体的实现中,继承的种类可以分为单重继承和多重继承。不同的开发语言支持的继承方式有所不同,如Java仅支持单继承,而C++则支持多继承。

继承有利于代码的复用,但也有些不足,主要体现在两个方面:一是继承提供机制,使得超类的BUG进一步被带到其子类;二是基于继承的代码的重用可能会导致维修困难,这是主要的设计质量问题。实践证明,继承运用得当,正确使用继承的设计对类的测试是有利的。从继承的不足可以看出,测试变得更加重要了,但对于测试来说,继承却增加了测试的复杂性。这当然主要是针对子类的测试,它不但要求那些子类新定义或重新定义的方法要得到测试,被子类继承的超类方法也往往要在子类的环境中重新测试,同时,继承使源代码变得难于理解。一个深层次的最底层的子类可能只有一两行代码,但却继承了上百种特征。多重继承会显著地增加派生类的复杂程度,导致一些难以发现的隐含错误。

在对子类进行测试时需要将超类的特征也包含在内,因此可以将测试状态空间分为两个部分,被继承的超类的状态空间和由子类定义或重新定义的状态空间,这两部分之间是正交的关系。将这两部分合而为一(展平),生成的平坦的状态模型是对子类进行状态测试的依据。在测试过程中,虽然继承带来了许多复杂性,但是针对继承自超类的方法、属性,依然可以利用继承特性进行测试用例的复用,以提高测试的效率。下面我们通过一个实例来说明。本案例中,要测试两个类:SuperClass和SubClass。其中SubClass由SuperClass派生,SuperClass为超类,SubClass为子类,图9-1-2为其类图。SuperClass完成整数加法和减法两个运算,SubClass在超类的基础上增加整数的乘法运算。图9-1-2SuperClass和SubClass类图在测试时首先可以针对超类SuperClass进行测试,建立相应的测试用例类。接下来再进行子类SubClass的测试。在对SubClass测试时,由于SubClass继承了SuperClass的方法op1(),所以在建立SubClass的测试用例类,都可以继承SuperClass的测试用例类。但是在SubClass的具体实现中其覆盖了超类的方法op2(),所以在SubClass测试用例类中也要覆盖超类测试用例类中对方法op2()的测试方法。其继承关系如图9-1-3所示。图9-1-3超类子类及测试类UML类图关系下面是超类SuperClass的代码实现,在SuperClass中包含两个方法op1()和op2(),这些方法分别实现了加法和减法运算。

/**

*超类完成加法和减法

*@authorAdministrator

*2008-7-22

*/

publicclassSuperClass{

/**

*加法运算

*@parama加数a*@paramb加数b

*@return返回a+b

*/

publicintop1(finalinta,finalintb){

returna+b;

}

/**

*减法运算

*@parama被减数a

*@paramb减数b

*@return返回a-b*/

publicintop2(finalinta,finalintb){

returna-b;

}

}子类SubClass在实现时继承自SuperClass,它在超类的基础上实现了方法op3(),完成乘法功能,并且对超类SuperClass的方法op2()进行覆盖,将在超类中对方法op2()进行改写,也同样实现了减法,但交换了减数和被减数的位置。具体代码如下:

/**

*子类,其中扩展乘法运行,改写了减法运算

*@authorAdministrator

*2008-7-22

*/

publicclassSubClassextendsSuperClass{

/**

*乘法运行

*@parama乘数a

*@paramb乘数b

*@return返回a*b

*/

publicfinalintop3(finalinta,finalintb){

returna*b;

}

/**

*减法运算*@paramb被减数a

*@parama减数b

*@return返回b-a

*/

publicintop2(finalinta,finalintb){

returnb-a;

}

}接下来开始建立测试类。首先测试超类,在测试中,分别对方法op1()和op2()进行功能测试。该代码仍然在JUnit下的TestCase,具体代码如下:

importjunit.framework.TestCase;

importorg.junit.After;

importorg.junit.Assert;

importorg.junit.Before;

importorg.junit.Test;

publicclassSuperClassTestextendsTestCase{

SuperClasssuperclass;

@Before

接下来测试SubClass,子类SubClass的测试用例类建立继承自SuperClass的测试类SuperClassTest,并在其基础上,增加了对方法op3()的测试,以及改写对方法op2()的测试。为了复用SuperClass的测试用例,在创建子类SubClass对象时,还需要对超类SuperClassTest继承来的对象SuperClass进行赋值操作。具体的代码如下:

importorg.junit.After;

importorg.junit.Assert;

importorg.junit.Before;

importorg.junit.Test;

publicclassSubClassTestextendsSuperClassTest{9.1.4多态和动态绑定对测试的影响

多态性和动态绑定(DynamicBinding)是面向对象的关键特性之一。多态性是指同一消息可以根据发生消息的对象不同而采取不同的处理方法,例如操作“move”,可以是自行车对象的行为,可以是飞机对象的行为,也可以是窗口对象的移动行为。多态的具体表现形式包括两类:一是重载,它允许几个函数有相同的名字,而所带的参数类型、数量不同,在系统运行时自动选择不同的实现方法;二是在类的继承上进行方法覆盖,即同样的消息可以被送到一个父类和它的子类对象上,在不同的对象上对一个类的操作是可以完全不同的。面向对象程序设计中,利用这种多态来提高程序的抽象性,突出语言的继承性。它大大提高了程序的抽象程度和简洁性,更重要的是它最大限度地降低了类和程序模块之间的耦合性,提高了类模块的封闭性,使得它们不需了解对方的具体细节,就可以很好地共同工作。这个优点对程序的设计、开发和维护都有很大的好处。

但是多态使得系统在运行时能自动为给定的消息选择合适的实现代码,它所带来的不确定性也使得传统测试实践中的静态分析法遇到了不可逾越的障碍,同时它们也增加了系统运行中可能的执行路径,加大了测试用例的选取难度和数量。这种不确定性和骤然增加的路径组合给测试覆盖率的满足带来了挑战。面向对象技术提高了软件系统的灵活性、一般性和生产率,同时也增加了软件测试的难度和复杂性。面向对象的软件测试作为面向对象软件工程的重要过程,它同样也要求用面向对象技术去测试,根据面向对象的特点,以对象为中心的软件测试。

9.2面向对象软件测试的层次

软件测试层次是基于测试复杂性分解的思想,是软件测试的一种基本模式。面向对象程序的结构不再是传统的功能模块结构,作为一个整体,原有的集成测试所要求的逐步将开发的模块组装在一起进行测试的方法已成为不可能。而且,面向对象软件抛弃了传统的开发模式,对每个开发阶段都有不同以往的要求和结果,已经不可能用功能细化的观点来检测面向对象分析和设计的结果。因此,传统的测试模型对面向对象软件已经不再适用。在面向对象软件测试中,继承和聚合关系刻画了类之间的内在层次,它们既是构造系统结构的基础,也是构造测试结构的基础。根据测试层次结构,面向对象软件测试总体上呈现从单元级、集成级到系统级的分层测试结构。其根据测试层次结构确定相应的测试活动,并生成相应的层次。由于面向对象软件从宏观上来看是各个类之间的相互作用,可以将对类层的测试作为单元测试,而对于由类簇集成的模块测试对应到集成测试,系统测试与传统测试层相同。具体的层次关系如表9-2-1所示。

1.类测试

类测试又可以分为两级:一是方法级测试,二是类级测试。两者测试的重点有所不同。

方法级测试重点在于测试封装在类中的每一个方法。这些方法关系到对类的数据成员所进行的操作。方法级测试可以采用传统的模块测试方法,但方法是封装在类中,并通过向所在对象发消息来执行。它的执行与状态有关,特别是在操作的多态性时,设计测试用例时要考虑设置对象的初态,并且要设计一些函数来观察隐蔽的状态值。测试方法主要是根据传统的单元测试方法,即运用前面介绍的面向对象测试中的一些基于面向对象特点的技术。类级测试重点在于测试同一类中不同方法之间的交互关系。面向对象的类测试主要考察封装在一个类中的方法和类的状态行为。进行类测试时要把对象与其状态结合起来,进行对象状态行为的测试,因为工作过程中对象的状态可能会被改变,而产生新的状态。测试范围主要是类定义之内的属性和服务,以及有限的对外接口的部分。在类测试过程中,不能仅仅检查输入数据产生的结果是否与预期的吻合,还要考虑对象的状态,整个过程应涉及对象的初态、输入参数、输出参数以及对象的终态。

2.类簇测试(集成测试)

我们把一组相互有影响的类看作一个整体,称为类簇。类簇测试主要根据系统中相关类的层次关系,检查类之间的相互作用的正确性,即检查各相关类之间消息连接的合法性、子类的继承性与父类的一致性、动态绑定执行的正确性、类簇协同完成系统功能的正确性等等。其测试有两种不同策略,即基于类间协作关系的横向测试和基于类间继承关系的纵向测试。

1)基于类间协作关系的横向测试

由系统的一个输入事件作为激励,对其触发的一组类进行测试,执行相应的操作、消息处理路径,最后终止于某一输出事件。应用回归测试对已测试过的类集再重新执行一次,以保证加入新类时不会产生意外的结果。

2)基于类间继承关系的纵向测试

首先通过测试独立类(指系统中已经测试正确的某类)来开始构造系统,在独立类测试完成后,下一层继承独立类的类(称为依赖类)被测试,这个依赖类层次的测试序列一直循环执行到构造完整个系统。

3.系统测试

系统测试是对所有程序和外部成员构成的整个系统进行整体测试,检验软件和其他系统成员配合工作是否正确,另外,还包括了确认测试内容,以验证软件系统的正确性和性能指标等是否满足需求规格说明书所制定的要求。它与传统的系统测试一样,可沿用传统的系统测试方法。

在整个面向对象的软件测试过程中,集成测试可与单元测试同时进行,以减少单元集成时出现的错误。对已经测试通过的单元,在集成测试或系统测试中,可能发现独立测试没有发现的错误。

Perry和Kaiser等通过研究weyuker的测试数据集充分性公理得出了以下几个与面向对象程序有关的测试公理,在测试中我们应该予以遵守。

(1)反合成性公理:对程序的各个独立部分单独进行了充分的测试并不表明整个软件得到了充分的测试。这是因为当这些独立部分交互时会产生它们在隔离状态下所不具备的新的分支。

(2)反分解性公理:对程序的整体进行了充分的测试并不表明程序的各个独立部分都得到了充分的测试。这是因为这些独立的部分有可能被用在其他的环境中,在这种情况下就需要在这种环境中对这个部分重新进行测试。

(3)反扩展性公理:对一个程序进行的充分性测试并不一定能使另一个相似的程序也得到充分的测试。这是因为两个相似的程序可能会具有完全不同的实现。

9.3面向对象的单元测试

传统软件的基本构成单元为功能模块,每个功能模块一般能独立地完成一个特定的功能。而在面向对象的软件中,基本单元是封装了数据和方法的类和对象。对象是类的实例,有自己的角色,并在系统中承担特定的责任。对象有自己的生存周期和状态,状态可以演变。对象的功能是在信息的触发下,实现对象中若干方法的合成以及与其他对象的合作。对象中的数据和方法是一个有机整体,面向对象的单元测试的类测试分两个部分:一是以方法为单元,另一种是以类为单元。但无论是哪种级别,所设计的测试用例,建议都以测试类的形式来组织,避免针对同一类设计的测试用例过于分散。9.3.1以方法为单元

类的行为是通过其内部方法来表现的,方法可以看作传统测试中的模块。简单地说,这种方法与传统测试方法中的单元测试方法类似。因此,传统针对模块的设计测试案例的技术例如逻辑覆盖、等价划分、边界值分析和错误推测等方法,仍然可以作为测试类中每个方法的主要技术。面向对象中为了提高方法的重用性,每个方法所实现的功能应尽量小,每个方法常常只由几行代码组成,控制比较简单,因此测试用例的设计相对比较容易。基于方法的单元测试需要桩和驱动器测试方法。另外,封装将数据、操作等集成在一个相对独立的程序单元——类中,类中方法的执行离不开一定的对象环境。测试类中的任何一个方法都必须首先将这个类实例化。在具体的测试过程中,方法封装在类中并通过向所在对象发消息来执行,它的执行与状态有关。具体过程如图9-3-1所示。因此在具体的测试设计过程中,应注意针对不同的状态设计测试用例来测试类的成员方法。图9-3-1面向对象的单元测试模型9.3.2以类为单元

面向对象软件中,在保证单个方法功能正确的基础上,还应该测试方法之间的协作关系。所以在类测试过程中还需要将整个类作为测试单元进行测试,用来测试某一公有方法与类中其他直接或间接调用的方法间的协作和交互情况,它类似于过程式语言中的集成测试。

以类为单元的测试方法可以沿用传统的过程模型的集成测试方法。在这里的集成范围被控制在测试类中方法间的协作交互。除此之外,在面向对象的单元测试中,还可以采用其他的测试方法,这里介绍基于状态图的类测试方法。对象状态测试是面向对象软件测试的重要部分,同传统的控制流和数据流测试相比,它侧重于对象的动态行为,这种动态行为依赖于对象的状态。通过测试对象动态行为,我们能检测出对象成员函数之间通过对象状态进行交互时产生的错误。因为对象的状态是通过对象数据成员的值反映出来的,所以检查对象的状态实际上就是跟踪被监视对象数据成员的值的变化。如果某个方法执行后对象的状态未能够按预期的方法改变,则说明该方法中含有错误。下面分步来介绍基于状态图的类测试。

1.状态转移图

类是面向对象程序的静态部分,对象是动态部分。对象的行为主要决定于对象状态和对象状态的转移。面向对象设计方法通常采用状态转移图建立对象的动态行为模型。状态转移图用于刻画对象响应各种事件时状态发生转移的情况,图中节点表示对象的某个可能状态,节点之间的有向边通常用“事件/动作”标出。状态转移图中的节点代表对象的逻辑状态,而非所有可能的实际状态。如图9-3-2的示例中,表示当对象处于状态A时,若接收到事件event则执行相应的操作action且转移到状态B。因此,对象的状态随各种外来事件发生怎样的变化,是考察对象行为的一个重要方面。其中A、B表示两种状态,event表示收到的事件。图9-3-2对象—状态转移图

2.测试方法

基于状态的测试是通过检查对象的状态在执行某个方法后是否会转移到预期状态的一种测试技术。使用该技术能够检验类中的方法是否正确地交互,即类中的方法是否能通过对象的状态正确地通信。 理论上讲,对象的状态空间是对象所有数据成员定义域的笛卡尔乘积。当对象含有多个数据成员时,对对象所有的可能状态进行测试是不现实的,这就需要对对象的状态空间进行简化,同时又不失对数据成员取值的“覆盖面”。简化对象状态空间的基本思想类似于黑盒测试中常用的等价类划分法。依据软件设计规范或分析程序源代码,可以从对象数据成员的取值域中找到一些特殊值和一般性的区间。特殊值是设计规范里说明有特殊意义、在程序源代码中逻辑上需特殊处理的取值。位于一般性区间中的值不需要区别各个值的差别,在逻辑上以同样方式处理。 进行基于状态的测试时,首先要对受测试的类进行扩充定义,即增加一些用于设置和检查对象状态的方法。通常是对每一个数据成员设置一个改变其取值的方法。另一项重要工作是编写作为主控的测试驱动程序,如果被测试的对象在执行某个方法时还要调用其他对象的方法,则需编写桩程序代替其他对象的方法。

测试过程为:首先生成对象;接着向对象发送消息把对象状态设置到测试实例指定的状态;再发送消息调用对象的方法;最后检查对象的状态是否按预期的方式发生变化。

3.测试步骤

下面给出基于状态转移图的类测试的主要步骤:

(1)依据设计文档或者通过分析对象数据成员的取值情况,导出对象的逻辑状态空间,得到被测试类的状态转移图。

(2)给被测试的类加入用于设置和检查对象状态的新方法。

(3)对于状态转移图中的每个状态,确定该状态是哪些方法的合法起始状态,即在该状态时,对象允许执行哪些操作。

(4)在每个状态中,从类中方法的调用关系图最下层开始,逐一测试类中的方法,测试每个方法时,根据对象当前状态确定出对方法的执行路径有特殊影响的参数值,将各种可能组合作为参数进行测试。

4.测试用例的生成

对于基于状态的类测试方法可采用深度或广度测试的方法先建立扩展树,树的节点表示状态,边表示状态之间的转移。根据树中的一条路径(从根节点到叶节点)来生成测试用例。如图9-3-3所示,将通过转移a到状态S1,根据S1状态按照广度扩展建立扩展树。图9-3-3面向对象单元测试状态转移扩展树的生成示意图状态及转移扩展树 9.4面向对象的集成测试

基于单元测试对成员函数行为正确性的保证,而集成测试却只关注系统的结构和内部的相互作用。对较大规模软件系统的集成测试更是一项复杂的系统工程。面向对象的集成测试的任务是检测出哪些类相互作用时才会产生的错误。测试的对象以类簇为单位,包括基于类间协作关系的横向测试和基于类间继承关系的纵向测试。而面向对象的集成测试中,类簇中类与类之间关系较复杂,对面向对象程序的静态表示,已不是一个树型层次结构,而是一个错综复杂的网状结构,这就决定了传统的基于层次结构的集成测试策略已不适用于面向对象程序的集成测试。因此,需要研究适应面向对象程序特点的新的集成测试策略。下面介绍基于UML的集成测试、具有MM-路径的集成和基于数据流的面向对象的集成测试方法。9.4.1基于UML的集成测试

1.基于UML协同图的分解的集成测试方法

协同图显示的是类自己的(部分)信息传输。在图中反映了类直接方法的调用情况。类似于前面传统集成所介绍的调用图。由此,面向对象的协同图也支持成对集成和相邻集成的方法。下面以一个模型为例,在该模型中,我们需要测试一个鸭子的外卖店DuckStor,该店可以根据用户要求制作duck并提供外送业务。但用户下了订单orderDuck后,根据用户要求DuckStor可以createDuck,这里提供3种地方特产的鸭子,四川的、北京的、上海的,每种鸭子需要不同的配方,所以根据鸭子类型的不同createDirection。同时DuckStor还可以直接查询配方getXXDirection。图9-4-1就是多个类的协同关系图。可以根据这些协同关系来设计集成测试的方案,其实质就是基于调用的集成,可以采用成对集成和相邻集成的方法。图9-4-1“鸭子的外卖店”类的协同关系图

2.基于UML序列图分解的集成测试方法

UML顺序图是按时间的顺序来描述对象之间交互的模型。序列图主要用于按照交互发生的一系列顺序,显示对象之间的这些交互。开发者一般认为序列图只对他们有意义,然而,业务人员会发现,序列图显示不同的业务对象如何交互,对于交流当前业务如何进行很有用。序列图对于测试人员也非常有用。在顺序图中,如果消息message1出现在消息message2的上面,则message1一定在message2之前被发送。在测试中引入UML顺序图,我们只需要按照序列图表示的消息顺序来测试对象类之间的交互即可。对于每一个类与其他类,它们之间的对象与对象的调用路径都可以用顺序图表现出来,只要我们的测试用例覆盖了该类与其他类调用的所有顺序图,我们就认为用这些测试用例集完全覆盖了该类与类的集成关系,也就是说该类与其他类之间进行集成的接口已经完全被测试过了。对于基于顺序图的测试,其测试用例基本是根据顺序图来设计的。顺序图在实质上就是按照一定前置条件和后置条件排列好的交互系列。在这个交互系列中,只有前置条件和后置条件都为真时才能进行下一步交互。只要前置条件或后置条件有一个为假,则交互都不能顺利进行或者说交互不正确。对于前置条件和后置条件为假的交互,我们可以统一按相同的方法来对待。同样以鸭子的外卖店DuckStor为例,序列图如图9-4-2所示。从其序列图中我们可以看出,通过测试驱动模块下达Order后DuckStor的活动序列。图9-4-2中仅表示出了订购一只四川鸭子的过程,其消息的传递过程都在图中表示出来了。在集成测试时可以根据这个消息的传递过程来选择集成的对象。另外还可以看出鸭子切片的活动在鸭子生产出来后才能进行。也就是说,制作鸭子的活动是切鸭子活动的前提条件。图9-4-2“鸭子的外卖店”活动序列9.4.2基于MM-路径的集成测试

在传统的软件的MM-路径测试中,我们通过消息来表示单元(模块)之间的调用,采用模块执行路径取代完整的模块。在面向对象的测试中也可以使用同样的方法来表示由消息分开的各种方法执行序列。与传统软件一样,方法也可能有多条内部执行路径。MM-路径从某个方法开始,当到达某个自己不发送任何消息的方法时结束。这就是消息的静止点。

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论