第八章面向对象软件测试一、面向对象测试概述1.传统的软件开发存在的问题(1)软件重用性差(2)软件可维护性差(3)开发出的软件不易满足用户需求一、面向对象测试概述2、面向对象技术基本概念(1)对象(2)对象的状态和行为(3)类(4)类的结构(类间关系)(5)消息和方法一、面向对象测试概述•我们生活在一个对象的世界里,每个对象有一定的属性,把属性相同的对象进行归纳就形成类•例如:家具就可以看作类,其主要的属性有价格、尺寸、重量、位置和颜色等•无论我们谈论桌子、椅子还是沙发、衣橱,这些属性总是可用的,因为它们都是家具而继承了为类定义的所有属性。•除了属性之外,每个对象可以被一系列不同的方式操纵,它可以被买卖、移动、修改(如漆上不同的颜色)。•这些操作或方法将改变对象的一个或多个属性。•类的合法操作可以和对象的定义联系在一起,并且被类的所有实例继承。一、面向对象测试概述面向对象特征(1)对象唯一性(2)分类性(3)继承性(4)多态性一、面向对象测试概述面向对象要素(1)抽象(2)封装(3)共享面向对象软件开发方法与传统的软件开发方法的区别:(1)调查、分析系统需求,建立一个全面、合理、统一的模型。(2)对象设计。(3)程序实现。面向对象软件开发过程面向对象的开发模型突破了传统的瀑布模型,将开发分为面向对象分析(OOA),面向对象设计(OOD),和面向对象编程(OOP)三个阶段。针对这种开发模型,结合传统的测试步骤的划分,我们把面向对象的软件测试分为:面向对象分析的测试,面向对象设计的测试,面向对象编程的测试;面向对象单元测试,面向对象集成测试,面向对象系统测试。面向对象测试在传统的面向过程程序中,对于函数y=Function(x);只需要考虑一个函数(Function())的行为特点,在面向对象程序中,你不得不同时考虑基类函数(Base::Function())的行为和继承类函数(Derived::Function())的行为。面向对象程序的结构不再是传统的功能模块结构,作为一个整体,原有集成测试所要求的逐步将开发的模块搭建在一起进行测试的方法已不可能。面向对象测试面向对象软件对每个开发阶段都有不同以往的要求和结果,已经不可能用功能细化的观点来检测面向对象分析和设计的结果。针对面向对象软件的开发特点,应该有一种新的测试模型。二、面向对象模型OOAOODOOPOOATestOODTestOOPTestOOUnitTestOOIntegrateTestOOSystemTestOOATest:面向对象分析的测试,OODTest:面向对象设计的测试,OOPTest:面向对象编程的测试;OOUnitTest:面向对象单元测试,OOIntegrateTest:面向对象集成测试,OOSystemTest:面向对象系统测试。二、面向对象模型OOATest和OODTest:是对分析和设计结果的测试,主要是对分析设计生成的文档进行,是软件开发前期的关键性测试;OOPTest:主要针对编程风格和程序代码实现进行测试看,其测试内容主要在面向对象单元和面向对象集成测试中体现;OOUnitTest:对程序内部具体单一的功能模块的测试。主要是对类的测试。OOIntegrateTest:主要对系统内部相互服务进行测试,如方法间的相互作用,类间的消息传递等。OOSystemTest:是面向对象测试的最后阶段的测试,主要以用户需求为测试标准,借鉴OOA及其测试结果。二、面向对象模型OOA:将问题空间中实现的功能进行抽象,问题空间中的实例抽象为对象,用对象的结构反映问题空间的复杂关系,用属性和服务表示实例的特殊性和行为OOD:建立类结构或进一步构造类库,实现分析结果对问题空间的抽象。OOP:软件的计算机实现。三、面向对象分析和设计的测试OOATest和OODTest:是对分析和设计结果的测试,主要是对分析设计生成的文档进行,是软件开发前期的关键性测试;OOA直接映射问题空间,全面地在问题空间中实现功能的现实抽象化。OOA必须回答:(1)为完成用户要求,系统应提供哪些功能(2)系统应由哪些对象构成(3)每个对象应有哪些属性和服务(4)对象间应有怎样的联系三、面向对象分析和设计的测试面向对象设计(OOD)采用“造型的观点”,以OOA为基础归纳出类,并建立类结构或进一步构造成类库,实现分析结果对问题空间的抽象。OOD归纳的类,可以是对象简单的延续,可以是不同对象的相同或相似的服务。由此可见,OOD不是在OOA上的另一思维方式的大动干戈,而是OOA的进一步细化和更高层的抽象。面向对象设计(OOD)是以OOA归纳出的类为基础,建立类结构甚至进一步构造成类库,实现分析结果对问题空间的抽象。三、面向对象分析和设计的测试对认定的对象的测试OOA(面向对象分析)中认定的对象是对问题空间中的结构、其他系统、设备、被记忆的事件、系统涉及的人员等实际实例的抽象。对它的测试可以从如下方面考虑:认定的对象是否全面,其名称应该尽量准确、适用,是否问题空间中所涉及到的实例都反映在认定的抽象对象中。认定的对象是否具有多个属性。只有一个属性的对象通常应看作其他对象的属性而不是抽象为独立的对象对认定为同一对象的实例是否有共同的、区别于其他实例的共同属性,是否提供或需要相同的服务如果系统没有必要始终保持对象代表的实例信息,提供或者得到关于它的服务,认定的对象也无必要。三、面向对象分析和设计的测试对认定的结构的测试认定的结构指的是多种对象的组织方式,用来反映问题空间中的复杂实例和复杂关系。认定的分类结构测试要点:处于高层的对象,是否在问题空间中含有不同于下一层对象的特殊可能性,即是否能派生出下一层对象。处于同一低层的对象,是否能抽象出在现实中有意义的更一般的上层对象。对所有认定的对象,是否能在问题空间内向上层抽象出在现实中有意义的对象。高层的对象的特性是否完全体现下层的共性,低层的对象是否有高层特性基础上的特殊性。三、面向对象分析和设计的测试对构造的类层次结构的测试为了能充分发挥面向对象继承共享特性,OOD(面向对象设计)的类层次结构通常基于OOA中产生的分类结构的原则来组织,着重体现父类和子类间的一般性和特殊性。在当前的问题空间,对类层次结构的主要要求是能在解空间构造实现全部功能的结构框架。为此测试要注意如下几个方面:类层次结构是否涵盖了所有定义的类;是否能体现OOA中所定义的实例关联、消息关联;子类是否具有父类没有的新特性;子类间的共同特性是否完全在父类中得以体现。五、面向对象编程的测试典型的面向对象程序具有继承、封装和多态的新特性,这使得传统的测试策略必须有所改变。封装是对数据的隐藏,外界只能通过被提供的操作来访问或修改数据,这样降低了数据被任意修改和读写的可能性,降低了传统程序中对数据非法操作的测试。继承是面向对象程序的重要特点,继承使得代码的重用率提高,同时也使错误传播的概率提高。五、面向对象编程的测试继承使得传统测试遇见了这样一个难题:对继承的代码究竟应该怎样测试?多态使得面向对象程序对外呈现出强大的处理能力,但同时却使得程序内“同一”函数的行为复杂化,测试时不得不考虑不同类型具体执行的代码和产生的行为。面向对象程序是把功能的实现分布在类中。能正确实现功能的类,通过消息传递来协同实现设计要求的功能。正是这种面向对象程序风格,将出现的错误能精确的确定在某一具体的类。因此,在面向对象编程(OOP)阶段,忽略类功能实现的细则,将测试的目光集中在类功能的实现和相应的面向对象程序风格,主要体现为以下两个方面(假设编程使用C++语言):☆数据成员是否满足数据封装的要求☆类是否实现了要求的功能六、面向对象的单元测试传统的单元测试是针对程序的函数、过程或完成某一定功能的程序块。沿用单元测试的概念,实际测试类成员函数。一些传统的测试方法在面向对象的单元测试中都可以使用。如等价类划分法,因果图法,边值分析法,逻辑覆盖法,路径分析法,等等,单元测试一般建议由程序员完成。六、面向对象的单元测试面向对象的单元测试的对象是软件设计的最小单位—类。单元测试的依据是详细设计,单元测试应对类中所有重要的属性和方法设计测试用例,以发现类内部的错误。单元测试多采用白盒测试技术,系统内多个类都可以并行进行测试。沿用单元测试概念,实际测试类成员函数。一些传统的测试方法在面向对象的单元测试中都可以使用,如等价类划分、边界值分析、因果图、逻辑覆盖、路径分析法等。1、单元测试的内容面向对象的单元就是类,单元测试实际测试的就是对类的测试。类测试的目的主要确保一个类的代码能够完全满足类的说明所描述的要求。2、单元测试开始的时间单元测试开始的时间一般在完全说明了这个类,并且准备对其编码后不久。单元测试开始时要制定一个测试计划。在反复迭代的过程中,类的实现和说明在进程中可能会发生变化,所以应该在软件的其他部件使用该类之前对类进行测试,同时还有必要执行回归测试。3、单元测试的人员由另一个类的开发人员编写测试计划,由该类的开发人员完成测试,避免对类说明的错误理解4、单元测试方法单元测试的方法有代码检查和执行测试用例。在某些情况下,用代码检查代替基于执行的测试方法是可行的,但是,代码检查也存在以下两个不利之处:代码检查容易受人为因素影响代码检查在回归测试方面明显需要更多的工作量类测试按顺序分为以下三部分:基于属性的测试:类中所有属性的设置和访问的测试。基于服务的测试:测试类中的每个方法。基于状态的测试:除了类的每个操作要进行测试,类的行为也要进行测试,所有能引起状态变化的事件都要模拟到。类的行为通常可用状态图来描述,在利用状态图进行类测试时,可考虑覆盖所有状态、状态迁移等覆盖标准,也可考虑从初始状态到终止状态的所有迁移路径的覆盖。5、方法的测试在测试类的功能实现时,应该首先保证类成员函数的正确性。测试时主要考虑封装在类中的一个方法对数据进行的操作,可以采用传统的模块测试方法,通过向所在对象发消息来执行,它的执行与状态有关。传统的针对模块的设计测试用例的技术,如等价划分、边界值分析、因果图、逻辑覆盖、路径覆盖等方法,仍然可以作为测试类中每个方法的主要技术。在面向对象的系统中的方法,是通过消息来驱动执行的,要测试类中的方法,必须用一个驱动程序对被测方法发送一条消息以驱动其执行,如果被测模块或方法中调用了其他模块或方法,则需要设计一个模拟被调子程序功能的存根程序,驱动程序、存根程序及被测模块或方法组成一个独立的可执行单元。在面向对象软件中,在保证单个方法功能正确的基础上,还应该处理好测试方法之间的协助关系。为了提高方法的重用性,设计方法的一个准则是提高方法的内聚,即在一个方法中只完成单个功能。对于继承过来的方法,也要加以测试。运行测试用例的时候,必须提供能够实例化的桩类,以及起驱动器作用的“主程序”类,来提供和分析测试用例。6、测试程度可以根据已经测试了多少类的实现和多少类的说明来衡量测试的充分性。通常需要将这两者都考虑到,希望测试到操作和状态转换的各种组合情况。一个对象能维持自己的状态,而状态一般来说也会影响操作的含义。类层次的分割测试这种测试可以减少用完全相同的方式检查类测试用例的数目。这很像传统软件测试中的等价类划分测试。分割测试又可分三种:基于状态的分割,按类操作是否改变类的状态来分割(归类);基于属性的分割,按类操作所用到的属性来分割(归类);基于类型的分割,按完成的功能分割(归类)。举例一个银行类Account:属性:balance(账户余额)和creditLimit(透支额)其操作有:open()打开账户、setup()建立、deposit()存款、withdraw()取钱、balance()查询余额、summarize()操作清单、creaditLimit()透支限额close()关闭账户举例基于状态划分就是根据它们改变类状态的能力对类操作进行划分。考虑Account类,状态操作包括deposit()、withdraw(),非状态操作包括balanc