测试驱动的面向对象软件开发 pdf-面向soa的事件驱动架构设计与实现
⑴ 以方法为单位可以将面向对象的单元测试归于传统的单元测试。 可以使用所有传统的功能和结构测试技术。 前期工作相对容易,但后续测试工作繁重。 (2)以类为单元的面向对象测试的层次取决于单元的构成,一般采用三层或四层的方式。 如果将单个操作或方法视为一个单元,则有四层测试,即操作/方法、类、继承和系统测试。 面向对象测试的主要问题是集成测试,可以看作是第三层,即通过测试的类之间的交互测试。 Junit简介 Junit是一个单元级测试工具,用于测试用Java语言编写的面向对象程序。 (由Eric Gamma和Kent Beck撰写,由SourceForge发布,使用许可遵循IBM的commonPublic License Version 1.0公共版权规范) *优点*: 1.在提高程序代码质量的同时,Junit测试使我们能够更快地编写程序。 2. Junit 易于使用 3. Junit 可以检查测试结果并提供即时反馈。 4. Junit 测试可以组织成分层测试系列架构。 5、使用Junit开发和测试成本低。 6. Junit测试提高了软件的稳定性。 7. Junit测试使用Java语言开发。 8. Junit 是免费的。
1、Junit安装配置(略) 2、Junit卸载步骤(略) 3、目前支持Junit的Java IDE主要有Forte for Java 3.0企业版; JBuilder 6企业版; Visual Age for Java等 四、Junit的各种断言 Junit提供了一些辅助功能来帮助我们判断一个被测试的功能是否正常工作,这些功能通常被称为断言。 你可以判断某个条件是否成立; 两个数据是否相等。 下面介绍一些断言方法: ⑴AssertEquals([String message], expected, actual) expected参数表示期望值; 实参代表被测代码实际产生的值; message 参数是可选的,通常用于报告错误消息。 ⑵assertNull/assertNotNull([String message],java.lang.Object object) 用于判断给定对象是否为空/(是否不为空),若为否则失败,message参数为也可选。 ⑶assertSame/assertNotSame([String message], expected, actual) 验证期望参数和实际参数是否引用/(不)同一个对象,如果答案为否,则答案失败,⑷assertTrue([String message] , 布尔条件) 验证给定的二进制条件是否为真。 如果它是假的,它将失败。 注意:当一个断言失败时,它所在的测试方法就会停止,也就是说,剩下的断言不会被执行。 应该在继续进行其他测试之前修复失败的测试。
另外,当某些测试失败时,不要向现有代码添加新功能! 此时测试驱动的面向对象软件开发 pdf,应尽快修复错误,直到所有测试通过。 5. Junit自动化测试框架的定义:是一种可以对代码进行单元测试的框架。 一个简单的自动化测试框架应该满足以下要求: 1. 能够将测试用例按照一定的方式组织成一个测试包,让所有的测试用例一次性执行,让实现者或测试者一键完成以完成所有测试工作,并输出明确的测试结果为目的。 2、支持简单操作,可以在测试包中添加任意数量的测试用例,不影响测试包的正常运行。 3.支持随机组合测试(一个测试包可以包含其他测试包)。 Junit的自动化测试框架如图-9: 图-9 Junit的自动化测试框架 Junit.Framework包中包含了Junit测试类所需的所有基类(Base Class)。 其实这个包也是整个Junit框架(Base_Framework)的基础。 TestCase类是这个包的核心,测试人员可以在继承TestCase类的基础上开发自己的测试驱动程序。 其余类用于支持TestCase类,其中TestSuite用于聚合多个测试用例(TestCase); Assert类用于验证期望值和实际值; TestResult类收集所有测试用例执行后的结果; Test接口建立了TestCase和TestSuite的关联,也为整个测试框架预留了扩展。
下面通过一段代码(包括前面介绍的断言)简单介绍一下Junit框架的使用。 第 1 行导入 junit.framework.*; //导入必要的Junit类库 2 public class TestSimple extends TestCase{3 public TestSimple(String name){4 super(name);5 } 6 public void testPlus(){ 7 assertEquals( 2,3-1); 8 } 9 }一个测试类会包含一些测试方法; 每个方法可以包含一个或多个断言语句,这些功能可以满足最基本的测试需求。 但有时你想在一个测试类中调用其他测试类,或者测试人员可能只想运行一个测试类中的某些方法。 这可以通过创建测试套件来实现。 例如下面这个类似TestSimple的测试驱动类,两者的区别在于增加了一个静态的Test suite方法,可以通过suite()方法返回任何需要的测试集合(没有suite()测试类中的方法,Junit 会自动运行所有以 test 开头的方法)。
代码如下: Line 1 import junit.framework.*;2 public class TestSimple extends TestCase{3 public TestSimple(String name){4 super(name);5 } 6 public void testPlus(){ 7 assertEquals(2, 3-1); 8 } 9 public void testAdd(){ 10 assertEquals(4,2+2);}public void testMultiple(){assertEquals(4,2╳2);} 15 public static Test suite(){TestSuite suite=new TestSuite( ); Suite.addTest( new TestClassOne("testPlus")); Suite.addTest( 20 new TestClassOne("testAdd")); 返回套房; 独立地,为每个测试重置一些测试环境; 测试完成后释放一些资源。
Junit中的TestCase基类为我们提供了这样两个方法,可以用来建立和清理环境: protected void setUp(); 受保护的无效拆解(); (例子见教科书) 7. JUnit支持两种运行单个测试的方法:静态方法和动态方法。 1.静态方法是重写TestCase类的runTest()方法。 一般以内部类的形式创建测试实例: TestCase test01 = new testCar("testgetWheels") {public void runTest() {testGetWheels();}} 2.动态方式是使用自省实现runTest () 创建测试实例。 这就要求测试的名称是需要调用的测试方法的名称:TestCase test01 = new testCar("testGetWheels"); 八、Testsuite的使用 在JUnit中,任何测试类都可以包含一个名为suite的静态方法,即:public static Test suite(); 在 suite() 方法中,将所需的测试实例添加到 TestSuite 对象并返回 TestSuite 对象。
因为 TestSuite 和 TestCase 都实现了 Test 接口,而 Test 接口定义了运行测试所需的方法。 因此,在测试类执行过程中,只能运行添加到TestSuite中的测试,不需要运行我们还不需要的测试。 九、Junit中main方法的使用如下: public static void main(String[] args){ Junit.textui.TestRunner.run(TestdrawbhTest.class); //使用文本交互方式启动测试用例Junit.awtui。 TestRunner.run(TestdrawbhTest.class); //使用AWT图形交互方式启动测试用例 Junit.swingui.TestRunner.run(TestdrawbhTest.class); //使用Swing图形交互方式启动测试用例} 十、Mock对象 当一个方法依赖于其他难以控制的东西时,比如网络,数据库,甚至是servlet引擎,在中使用mock对象在调试期间以测试代替实物。 经验教训 测试是一个持续的过程。 通过编写测试代码,您将对类的行为有一个准确的了解,您将更快地编写出高效的工作代码。
编写测试代码的一些具体技巧或良好实践: 1、不要使用TestCase构造函数来初始化测试夹具,而是使用setUp()和tearDown()方法; 2. 不要依赖或假设测试运行顺序,因为JUnit 使用Vector 来存储测试方法。 因此,不同的平台会按照不同的顺序从Vector中取出测试方法; 3. 避免编写有副作用的TestCase。 例如:如果后续测试依赖于某些特定的交易数据,则不要提交交易数据。 简单的回滚就足够了; 4、在继承一个测试类的时候,记得调用父类的setUp()和tearDown()方法; 5.将测试代码和工作代码放在一起,同步编译更新(使用Ant中有支持junit的任务); 6.? 测试类和测试方法应该有一致的命名方案。 例如在工作类名前加上test,组成测试类名; 7、保证测试与时间无关,不依赖使用过期数据进行测试。 导致后续维护过程中难以重现测试; 8.如果你写的软件是面向国际市场的,写测试的时候要考虑国际化因素。 不要只使用母语的语言环境进行测试; 9、尽量使用JUnit提供的assert/fail方法和异常处理方法,使代码更简洁; 10. 测试应该尽可能小并且执行迅速。
本章小结 本章在分析面向对象软件测试和传统软件测试的基础上,简要介绍了面向对象软件测试的基本知识; 然后从不同层次分析面向对象软件测试技术,如:测试用例和测试驱动程序设计、测试抽象类和接口类、测试重载和覆盖等技巧。 介绍了面向对象单元测试的常用工具(junit),以及安装和卸载方法; 并通过多个实例详细介绍了基于junit编写测试驱动的方法和技巧。 练习 1. 面向对象的软件测试和传统的软件测试有什么区别? 2、通常类测试驱动是由测试人员开发的还是程序员开发的? 3、如何评价课堂测试的价值? 4.如何测试抽象类? 5.如何测试接口类? 6. 如何测试重载和覆盖? 7. 如何确定面向对象的测试等级? 8.如何安装和卸载Junit测试工具? 9、如何搭建和清理测试环境? 10、尝试设计一个使用Junit进行测试的例子。 面向对象软件测试 [本章重点] 面向对象软件测试与传统软件的区别; 班级试验值评价方法; 类测试用例设计方法; 特级测试方法; 面向对象软件测试的划分方法; 使用方法。 【本章目标】了解面向对象测试与传统软件测试的异同; 掌握课堂测试的基本知识; 重点了解抽象类、接口类等特殊类的测试技术,以及设计类测试用例和测试驱动程序的几种方法; 初步了解Junit,掌握该工具的安装和卸载方法,以及如何使用它进行简单测试。
面向对象测试与传统测试的比较 传统的计算机软件测试策略是从“小测试”开始,逐步走向“大测试”,单元测试的重点是最小的可编译程序单元——子程序。 面向对象程序的结构不再是传统的功能模块结构,而是一个整体,每个开发阶段都有不同的需求和结果。 设计的结果。 面向对象软件测试分为:面向对象分析测试、面向对象设计测试、面向对象编程测试、面向对象单元测试、面向对象集成测试、面向对象系统测试。 1、传统的面向过程分析和面向对象分析(OOA) 2、结构设计方法和面向对象设计(OOD) 3、典型的面向对象程序具有继承、封装和多态的新特点。 4、传统单元测试的对象是软件设计的最小单元——模块。 5、在传统的集成测试中,对集成功能模块的测试主要有两种方式:①自上而下的集成②自下而上的集成。 6、为保证软件的功能完整性测试驱动的面向对象软件开发 pdf,除了单元测试和集成测试外,还必须进行标准化的系统测试。 信息隐藏的重要功能之一是信息隐藏。 它控制对封装在类中的信息的访问,以防止类中的实现细节信息被错误使用。 这种隐蔽机制使测试变得困难。
封装和继承对测试的影响如果一个类已经过完整的测试,当它被子类继承时,需要重新测试被继承方法在子类环境中的行为特征。 多态性对测试的影响 传统的软件测试往往使用静态分析技术来分析代码; 在面向对象软件中,由于动态绑定和多态性带来的不确定性,测试覆盖率的满足增加了难度。 类测试基础 1、类测试的概念:验证一个类的实现是否与类的描述完全一致。 2、类测试的方法:通过代码检查或测试用例的执行等方式有效地进行类测试。 (后者优于前者)。 3. 类测试人员 类测试通常由开发人员进行; (会带来好处和缺点)。 4. 类测试时间类测试伴随开发过程的每个阶段,当类的描述或实现发生变化时应进行回归测试。 5.类测试过程为类创建一个实例-->创建一个合适的环境-->运行测试用例(向一个实例发送一条或多条消息)-->通过参数检查测试运行的结果-- > 清除执行测试用例所需的测试环境。 UML 类的描述 UML(Unified Modeling Language)语言是一种支持对象技术的建模语言。 它是一种在计算机系统中代表现实世界,描述现实世界中的对象及其关系,支持应用程序的语言。 发展。 在UML中,用来表示类的符号是一个矩形,分为三个区域,即: (1)名称区:显示类的名称 (2)属性区:显示类中定义的变量。
⑶ 操作区:显示类中定义的方法。 如图-1: 图-1 类的UML表示法 类之间的关系分为关联、泛化、实现、依赖、聚合和组合六类。 每个关系由不同的符号表示(如表1所示),类用三个关键字修饰:private、protected和public(如表2所示)。 表-1 UML类图符号说明 表-2 UML类图范围说明 类测试的价值 要选择是将每个类作为一个单元单独测试还是与其他类绑定进行集成测试,需要使用以下3个测试值的评估基于三个要素: 1.类本身的复杂程度 2.类在整个系统中的水平 3.开发此类测试驱动程序的成本 4.类的风险程度本身类测试用例设计一般是基于以下3种标准设计测试系列,即基于状态的覆盖、基于限制的覆盖和基于代码的覆盖。 测试用例的设计方法有很多种:(例子见教材) 1.根据前置条件和后置条件确定测试用例 2.根据状态转换确定测试用例 3.根据访问控制修饰符(限定类)确定测试用例、属性或方法是程序其他部分的访问和调用修饰符)来确定测试用例。 类测试驱动程序设计 从开发的角度:测试驱动的基本思想是在设计前考虑测试代码; 从测试的角度来看:为了执行测试,运行测试用例以找出软件中隐藏的错误。
因此,测试驱动的构建应该简单、透明、易于维护,能够提供尽可能多的服务,同时兼顾自增量更新,更理想的情况是能够复用代码现有的测试驱动程序。 编写类测试驱动程序的方法有很多种,这里以Java语言为例说明测试驱动程序设计的结构。 (主要以二票系统为例,详见教材) 1、在main方法中编写需要运行的测试用例,即实现main方法,然后编译执行该类。 2.在类中实现一个静态测试方法,通过调用该测试方法收集各个测试用例的执行结果。 3.实现一个独立的测试类,其职责是执行和收集每个测试用例的结果。 类测试的扩展本节将简要介绍类测试构造的思想以及如何测试接口类、抽象类等。 1.继承层次中类的测试继承是实现接口和代码重用的有效机制。 根据继承机制的特点,子类继承了父类中测试用例所测试的代码。 只要父类代码没有被子类“覆盖”,那么就没有必要重新创建这些测试用例。 图6 各种类型之间的继承关系: 图6 类之间的继承关系 Class_A类有两个实例方法operation1()和operation2(),Class_B类继承Class_A类并实现了一个新的实例方法operation3(),Class_C类继承了Class_B类,覆盖了Class_B类的实例方法operation3()和实例方法operation2()。 根据图6中这三个类的区别,可以判断被继承的测试用例是否需要生成新的子类测试用例,哪些测试用例适合测试子类,哪些测试用例不必在测试子类中执行,如下表7所示: 由此可以得出结论,继承层次用例中类测试的测试可以采用以下补充原则: 1)如果一个子类增加一个或多个新的操作,需要添加相应的测试用例。
2)如果子类定义的同名方法覆盖了父类的方法,需要添加相应的测试用例。 然后,在构造类测试用例时可以使用图7所示的结构。 对于基类,我们需要测试所有,底层测试类可以返回其父类的测试方法。 图-7 类测试用例的构建 2. 接口类测试 测试类时,需要构建一个可执行的类实例,但接口没有任何构造方法,无法实现。 由于接口必须在某个类中实现,所以使用实现接口的类进行测试。 遵循这些原则: 如果接口没有被任何类实现,就没有必要测试它。 如果由另一个类实现,则针对实现该接口的类进行测试。 (如图-8) 图8 InterFace接口测试类图 3.抽象类测试 如果要构造抽象类测试驱动,首先要继承测试驱动类,需要继承被测抽象类同时,因为这个类不能被指定改变。 但是Java采用单继承机制,所以这个抽象类的测试驱动不能同时继承两个抽象类。 通常,对抽象类的测试使用以下两种方法: 1.一般情况下,利用Java的内部Class机制,在抽象类的测试驱动中引入内部类,让内部类实现对类的继承抽象类进行测试,然后将其作为引用体,这样内部类的测试就等同于抽象类测试的测试。 示例如下: /*AbstractExample.java *Created date: *Created by: *Modified date: *Modified by: */ package applet.unedu.tlpe.test; public abstract class AbstractExample implements Example{ ... public AbstractExample(){ ... ... } ... } /* AbstractExampleTester.java *创建日期:*创建者:*修改日期:*修改者:*/ package applet.unedu. tlpe.test; public abstract class AbstractExampleTester extends TestCase{ static class AbstractExample_Inner extends AbstractExample{ public AbstractExample_Inner(){ super(argumentname); } } public AbstractExampleTester(){ super(argumentname); } public Example newObject(argumentname){ return new AbstractExample_Inner(); } public void testAbstractExample_Inner(){ ... } ... }2、如果抽象类被具体类继承,那么在创建具体类的测试驱动时,应该继承抽象类的测试驱动。 在以后的回归测试中,只要执行了最底层的测试类,父测试类就重新执行测试,分别返回测试结果。
4.重载和覆盖测试覆盖是在子类中重新定义从父类继承的同名方法; 重载不同于覆盖,它不是子类对父类的同名方法的重新定义,而是类本身。 同名方法的重新定义。 在测试的过程中,可以参考以下两个原则: ① 所有重载形式的类实例方法都应该单独测试。 ② 子类的测试驱动继承父类的测试驱动时,需要测试覆盖父类的同名方法,并且要重新测试所有重载形式的类实例方法父类。 包 applet.unedu.tlpe.test; public class reload { public reload(){ } public int method_a(){ 返回“method_a()”; } public int method_a(String a){ 返回“method_a(String a)”; } public int method_a(String a,String b){ return "method_a(String a,String b)"; } public int method_b(){ 返回“method_b()”; } public int method_c(){ return "method_c() ”; } } reload_TestCase是reload类的测试驱动,对reload类包含的method_a实例方法的三种重载形式进行测试,代码如下:/ * reload_TestCase .java */ package applet.unedu.tlpe.test; public class reload_TestCase extends TestCase{ ..... public reload_TestCase(argument ){ ... } public void testMethod_a(){...} public void testMethod_a_a( ){...} public void testMethod_a_ab(){...} public void testMethod_b(){……} public void testMethod_c(){……}...} 5.异常测试 因为存在异常,所以在构造测试用例时,还需要考虑在某些特殊情况下如何测试程序代码是否返回指定状态 面向对象测试的层次 在传统软件中,确定一个单元的准则是: 1. 可以编译出的最小程序块本身; 2. 单一过程/功能(独立); 3. 完成的小规模工作由一个人。 面向对象软件测试与传统方法的区别:指南中没有明确说明是使用类还是方法作为单元。 下面简单介绍和比较以方法和类为单元的测试。