,有一款测试工具就很方便易用testwriter破解版下载,具体怎样使用,可以了解一下吗

查看:6695|回复:3
助理工程师
鉴于junit(框架)在android测试体系中占有重要的地位,因此在学习android过程中有必要对junit的源码有深入的了解;& && & 已经全面提供基于JUNIT的功能测试服务,支持Robotium及淘宝Athrun测试框架。点此体验
这份文档对junit的源码进行了比较详细的介绍,有需要的可以看看;
(751.21 KB, 下载次数: 525) ,以下为全文:
JUNIT 源码分析
整理:若言于至空间:引文:1、《JUnit 入门》,网上下载版,作者不详,共 18 页 word 文档,2758 字;2、《Java 开源 JUnit 教程》,网上下载版,作者不详,文中标题为《Java 开源测试工 具 JUnit 简介》;3、《Junit 设计模式分析》,刘兵,该文发表于《程序员》第 6 期;4、《Junit In Action 中文版》,V incent Massol 著,鲍志云译; 本文内容根据以上各文中的内容整理,未经过各个作者同意。1& &单元测试概述<font color="#.1 关于测试的几个概念白盒测试——把测试对象看作一个打开的盒子,程序内部的逻辑结构和其他信息对测试人员是公开的。回归测试——软件或环境的修复或更正后的“再测试”,自动测试工具对这类测试尤其 有用。单元测试——是最小粒度的测试,以测试某个功能或代码块。一般由程序员来做,因为 它需要知道内部程序设计和编码的细节。JUnit&&——是一个开发源代码的 Java 测试框架,用于编写和运行可重复的测试。他是用 于单元测试框架体系 xUnit 的一个实例(用于 java 语言)。主要用于白盒测试,回归测试。
<font color="#.2 单元测试概念<font color="#.2.1 单元测试的好处A、提高开发速度——测试是以自动化方式执行的,提升了测试代码的执行 效率。B、提高软件代码质量——它使用小版本发布至集成,便于实现人员除错。 同时引入重构概念,让代码更干净和富有弹性。C、提升系统的可信赖度——它是回归测试的一种。支持修复或更正后的“再测试”,可确保代码的正确性。<font color="#.2.2 单元测试针对对象A、面向过程的软件开发针对过程。B、面向对象的软件开发针对对象。C、可以做类测试,功能测试,接口测试(最常用于测试类中的方法)。<font color="#.2.3 单元测试工具和框架目前的最流行的单元测试工具是xUnit系列框架,常用的根据语言不同分 为JUnit(java),CppUnit(C++),DUnit (Delphi ),NUnit(.net), PhpUnit(Php )等等。该测试框架的第一个和最杰出的应用就是由Erich Gamma (《设计模式》的作者)和Kent Beck(XP(Extreme Programming) 的创始人 )提供的开放源代码的JUnit。2& &JUnit 简介<font color="#.1 JUnit的好处和JUnit单元测试编写原则好处: A、可以使测试代码与产品代码分开。 B、针对某一个类的测试代码通过较少的改动便可以应用于另一个类的测试。 C、易于集成到测试人员的构建过程中,JUnit和Ant的结合可以实施增量开发。 D、JUnit是公开源代码的,可以进行二次开发。 C、可以方便地对JUnit进行扩展。编写原则: A、是简化测试的编写,这种简化包括测试框架的学习和实际测试单元的编写。 B、是使测试单元保持持久性。C、是可以利用既有的测试来编写相关的测试。<font color="#.2 JUnit的特征
A、使用断言方法判断期望值和实际值差异,返回 Boolean 值。
B、测试驱动设备使用共同的初始化变量或者实例。
C、测试包结构便于组织和集成运行。
D、支持图型交互模式和文本交互模式。
由于才开始介绍Junit,所以可能这些语言描述比较难理解,所以我们等一下介绍一个简单的应用例子来讲述具体的情况。
系列教程:
3& &JUnit 简单应用实例<font color="#.1 JUnit安装步骤分解在Junit压 缩包解压到一个物理目录中(例如C:\Junit3.8.1)。记录Junit.jar文件所在目录名(例如C:\Junit3.8.1Junit.jar)。 进入操作系统(以Windows2000操作系统为准),按照次序点击“开始设置 控制面板”。 在控制面板选项中选择“系统”,点击“环境变量”,在“系统变量”的“变量”列表框中选择“CLASS-PATH”关键字(不区分大小写),如果该关键字 不存在则添加。双击“CLASS-PATH”关键字添加字符串“C:\Junit3.8.1Junti.jar”(注 意,如果已有别的字符串请在该字符串的字符结尾加上分号“;”),这样确定修改后Junit就可以在集成环境中应用了。对于 IDE 环境,对于需要用到的 JUnit 的项目增加到 lib 中,其设置不同的IDE 有不同的设置 。<font color="#.2 JUnit在Eclipse中的使用Eclipse 集成了 JUnit,可以非常方便地编写 TestCase。Eclipse 自带了一个 JUnit 的插件, 不用安装就可以在你的项目中开始测试相关的类,并且可以调试你的测试用例和被测试类。下面介绍一下 Eclipse 中 JUint 使用步骤:
a.创建一个新的 Java Project,File--&New--&Java Project;
b.输入 Project name&&之后点 Finish;
c.在 src 中新建一个 Class,New—&Class;
d.在 Name 栏中输入 Class 名,选中 public static void main(string[] args);
e.编辑一段代码,例如 DoubleAdd.java;
f.在 Eclipse 中加入一个 JUnit 库,先选择 Properties;
弹出 Properties fordoubleadd 对话框,选择 Java Build Path 中的 Libraries 标签,选
择 Add Library,弹出Add Library 对话框,选择 JUnit 点 Next;
这样就将一个 JUnit 3 的库加入到 doubleadd 中了;
g.新建一个 JUnit Test Case,New--&JUnit Test Case
弹出 New JUnit Test Case 对话框,输入 Name 选中 setUp()和 tearDown,点 Finish;
这样就新建了一个 JUnit Test Case
h.编辑一个 test case
i.然后运行 JUnit Test,得出结果,Run As--&JUnit Test;
如果 assert 成功,则会出现绿色的条带。
如果有任何一个方法没有 assert 成功,就会显示红色的条带,并再下面的
Failure Trace 中说明原因。以上就是一个简单的 JUnit 在 Eclipse 中使用的实例。4& &JUnit 核心类介绍
4.1 JUnit框架组成
在 junit3版本的类库如下所示:
其中 Junit 框架核心类有如下 7 个类(接口):
A、测试接口,定义了测试用例的接口(Test) B、对测试目标进行测试的方法与过程集合,可称为测试用例(TestCase)。
C、测试用例的集合,可容纳多个测试用例 (TestCase),将其称作测试包
(TestSuite)。 D、测试结果的描述与记录。(TestResult)。 E、测试过程中的事件监听者(TestListener)。
F、每一个测试方法所发生的与预期不一致状况的描述,称其测试失败元素
(TestFailure)
G、JUnitFramework 中的出错异常(AssertionFailedError)。
JUnit 框架是一个典型的Composite 模式:TestSuite 可以容纳任何派生自
Test 的对象;当调用TestSuite 对象的 run()方法是,会遍历自己容纳的对象,
逐个调用它们的 run()方法。
4.2 JUnit的运作模式
在以上的应用实例中,当你需要编写更多的 test case 的时候,你可以创建更多的 TestCase 对象。当你需要一次执行多个 TestCase 对象的时候,你可以创 建另一个叫做 TestSuite 的对象。为了执行 TestSuite,你需要使用 TestRunner。
如下图所示,展现了这 3 个类在实践中的三重唱:
这里,TestSuite 相当于是一个 TestCase的集合,TestRunner 相当于一个
测试工具,将 TestSuite 中的用例自动执行,同时利用 TestResult 收集测试结 果信息。具体的流程,可以参考其中的设计模式5& &JUnit 设计模式
以下内容,参考《Junit 设计模式分析》一文,这里仅全文引用该文中有关设计模式 的内容。
3& &JUnit 设计模式
3.1& & Command(命令)模式
3.1.1 问题
JUnit 是一个测试 framework,测试人员只需开发测试用例。然后把这些测试用例(TestCase) 组成请求(可能是一个或者多个),发送到 JUnit,然后由 JUnit 执行,最后报告详细测试结果。 其中包括执行的时间,错误方法,错误位置等。这样测试用例的开发人员就不需知道 JUnit 内部的细节,只要符合它定义的请求格式即可。从 JUnit 的角度考虑,它并不需要知道请求 TestCase 的具体操作信息,仅把它当作一种命令来执行,然后把执行测试结果发给测试人员。 这样就使 JUnit&&框架和 TestCase 的开发人员独立开来,使得请求的一方不必知道接收请求一 方的详细信息,更不必知道是怎样被接收,以及怎样被执行的,实现系统的松耦合。
3.1.2 模式的选择
Command(命令)模式(请参见 Gamma, E., et al. DesignPatterns: Elementsof Reusable Object-Oriented Software, Addison-Wesley, Reading, MA, 1995)则能够比较好地满足需求。摘 引其意图(intent),&将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参 数化;对请求进行排队或记录请求日志...&Command 模式告诉我们可以为一个操作生成一个 对象并给出它的一个&execute(执行)&方法。
3.1.3 实现
file:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image001.gif为了实现 Command 模式,首先定义了一个接口 Test,其中 Run便是 Command 的 Execute方法。然后又使用 Default Adapter模式为这个接口提供缺省实现的抽象类 TestCase,这样开 发人员就可以从这个缺省实现进行继承,而不必从 Test 接口直接实现。
我们首先来分析 Test&&接口,它有一个 countTestCases&&方法,用来统计这次测试有多少个TestCase,另外一个方法就是Command 模式的 Excecute 方法,这里命名为 run,参数 TestResult用来统计测试结果public interface Test {// Counts the number of test cases that will be run by this test. public abstract int countTestCases();//runs a test and collects its result in a TestResult instance.
TestCase 是该接口的抽象实现,它增加了一个测试名称属性,因为每一个 TestCase 在创建时 都要有一个名称,如果一个测试失败了,便可识别出是哪个测试失败public abstract class TestCase extends Assert implements Test {//the name of the test case private String fNpublic void run(TestResult result) {result.run(this);}}
这样测试人员,编写测试用例时,只需继承 TestCase,来完成 run 方法即可,然后JUnit 获 得测试用例的请求,执行它的run 方法,把测试结果记录在 TestResult 之中,目前可以暂且 这样理解。
3.1.4 效果
下面来考虑经过使用 Command 模式后给系统的架构带来了那些效果:
z Command 模式将实现请求的一方(TestCase 开发)和调用一方(JUnit&&)进行解藕
z& &&&Command 模式使新的 TestCase 很容易加入,无需改变已有的类,只需继承 TestCase 类 即可,这样方便了测试人员
z& &&&Command 模式可以将多个 TestCase 进行组合成一个复合命令,你将看到 TestSuit 就是 它的复合命令,当然它使用了 Composite 模式
z& &&&Command 模式容易把请求的 TestCase 组合成请求队列,这样使接收请求的一方(Junit Fromwork),容易决定是否执行请求,一旦发现测试用例失败或者错误可以立刻停止进 行报告
z Command 模式可以在需要的情况下,方便实现对请求的 Undo 和 Redo,以及记录 Log,
这部分目前在 JUnit 中还没有实现,将来是很容易加入的
3.2& &Composite(组合)
3.2.1 问题
为了获得对系统测试的信心,需要运行多个测试用例。通过使用 Command 模式,JUnit 能够方便的运行一个单独的测试用例之后产生测试结果。可是在实际的测试过程中,需要把 多个测试用例进行组合成为一个复合的测试用例,当作一个请求发送给 JUnit.这样 JUnit 就 会面临一个问题,必须考虑测试请求的类型,是一个单一的 TestCase 还是一个复合的TestCase,甚至要区分到底有多少个 TestCase。这样 Junit 框架就要完成像下面这样的代码:
if(isSingleTestCase(objectRequest)){
//如果是单个的 TestCase,执行 run,获得测试结果
(TestCase)objectRequest.run()
}else if(isCompositeTestCase(objectRequest)){
//如果是一个复合 TestCase,就要执行不同的操作,然后进行复杂的算法进行分
//解,之后再运行每一个 TestCase,最后获得测试结果,同时又要考虑
//如果中间测试出现错误怎么办????、
…………………………
…………………………
这会使 JUnit 必须考虑区分请求(TestCase)的类型(是单个testCase 还是复合 testCase), 而实际上大多数情况下,测试人员认为这两者是一样的。对于这两者的区别使用,又会使测 试用例的编写变得更加复杂,难以维护和扩展。于是要考虑,怎样设计 JUnit 才可以实现不需要区分单个 TestCase 还是复合 TestCase,把它们统一成相同的请求?
3.2.2 模式的选择
当 JUnit 不必区分其运行的是一个或多个测试用例时,能够轻松地解决这个问题的模式就 是 Composite(组合)模式。摘引其意图,&将对象组合成树形结构以表示'部分-整体'的层次 结构。Composite使得用户对单个对象和组合对象的使用具有一致性。&在这里'部分-整体' 的层次结构是解决问题的关键,可以把单个的 TestCase 看作部分,而把复合的TestCase 看 作整体(称为 TestSuit)。这样使用该模式便可以恰到好处得解决了这个难题。
首先看 Composite 模式的结构:
Composite 模式引入以下的参与者:
?& &&&Component:这是一个抽象角色,它给参加组合的对象规定一个接口。这个角色,给出共有的接口和默认行为。其实就我们的 Test接口,它定义出 run方法。?& &&&Composite:实现共有接口并维护一个测试用例的集合。就是复合测试用例 TestSuit
?& &&&Leaf:代表参加组合的对象,它没有下级子对象,仅定义出参加组合的原始对象的 行为,其实就是单一的测试用例TestCase,它仅实现 Test 接口的方法。
其实 componsite 模式根据所实现的接口类型区分为两种形式,分别称为安全式和透明式。
JUnit 中使用了安全式的结构,这样在 TestCase 中没有管理子对象的方法。
3.2.3 实现
composite 模式告诉我们要引入一个 Component 抽象类,为 Leaf 对象和 composite 对象 定义公共的接口。这个类的基本意图就是定义一个接口。在 Java 中使用 Composite 模式时,优先考虑使用接口,而非抽象类,因此引入一个 Test 接口。当然我们的 leaf 就是 TestCase 了。其源代码如下/composite 模式中的 Component 角色public interface Test {public abstract void run(TestResult result);}//composite 模式中的 Leaf 角色public abstract class TestCase extends Assert implements Test {public void run(TestResult result) {result.run(this);}}
下面,列出 Composite 源码。将其取名为 TestSuit 类。TestSuit 有一个属性 fTests&&(Vector类型)中保存了其子测试用例,提供 addTest 方法来实现增加子对象 TestCase ,并且还提供 testCount 和 tests&&等方法来操作子对象。最后通过 run() 方法实现对其子对象进行委托file:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image001.giffile:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif(delegate),最后还提供 addTestSuite 方法实现递归,构造成树形public class TestSuite implements Test { private Vector fTests= new Vector(10); public void addTest(Test test) {fTests.addElement(test);}public Enumeration tests() {return fTests.elements();}public void run(TestResult result) {for (Enumeration e= tests(); e.hasMoreElements(); ) { Test test= (Test)e.nextElement();runTest(test, result);}}public void addTestSuite(Class testClass) {addTest(new TestSuite(testClass));}}
分析了 Composite 模式的实现后我们列出它的组成,如下图:
注意所有上面的代码是对 Test 接口进行实现的。由于 TestCase 和 TestSuit 两者都符合 Test 接口,我们可以通过 addTestSuite递归地将 TestSuite 再组合成 TestSuite,这样将构成树形结构。所有开发者都能够创建他们自己的 TestSuit。测试人员可创建一个组合了这些测试用例的 TestSuit 来运行它们所有的 TestCasepublic static Test suite() {TestSuite suite1 = new TestSuite(&我的测试 TestSuit1&); TestSuite suite2 = new TestSuite(&我的测试 TestSuit2&);suite1.addTestSuite(untitled6.Testmath.class); suite2.addTestSuite(untitled6.Testmulti.class); suite1.addTest(suite2);return suite1;}
其组成结构如下图
3.2.4 效果
我们来考虑经过使用 Composite模式后给系统的架构带来了那些效果:
z& &&&简化了 JUnit 的代码& &JUnit 可以统一处理组合结构TestSuite 和单个对象 TestCase。使 JUnit 开发变得简单容易,因为不需要区分部分和整体的区别,不需要写一些充斥着 if else 的选择语句。z& &&&定义了 TestCase 对象和 TestSuite 的类层次结构& &基本对象 TestCase 可以被组合成更复杂的组合对象 TestSuite,而这些组合对象又可以被组合,如上个例子,这样不断地递归下去。在程序的代码中,任何使用基本对象的地方都可方便的使用组合对象,大大简化系统维护和开发。z& &&&使得更容易增加新的类型的 TestCase,如下面介绍的 Decorate 模式来扩展 TestCase 的功 能
3.3& & Template Method(模板方法)
3.3.1 问题在实际的测试中,为了测试业务逻辑,必须构造一些参数或者一些资源,然后才可进行测 试,最后必须释放这些系统资源。如测试数据库应用时,必须创建数据库连接 Connection, 然后执行操作,最后必须释放数据库的连接等。如下代码:public void testUpdate(){// Load the Oracle JDBC driver and Connect to the database DriverManager.registerDriver(new oracle.jdbc.OracleDriver()); String url = &jdbc:oracle:thin:@localhost:1521:ORA91&;Connection conn = DriverManager.getConnection (url, &hr&, &hr&);// Select first_name and last_name column from the employees tableResultSet rset = stmt.executeQuery (&select FIRST_NAME, LAST_NAME from EMPLOYEES&);……………// Disconnect conn.close ();}
其实这种情况很多,如测试 EJB时,必须进行 JNDI 的 LookUp,获得 Home 接口等。可是 如果在一个 TestCase 中有几个测试方法,例如测试对数据库的 Insert,Update,Delete,Select 等操作,这些操作必须在每个方法中都首先获得数据库连接 connection,然后测试业务逻辑, 最后再释放连接。这样就增加了测试人员的工作,反复的书写这些代码,与 JUnit 当初的设 计目标不一致?怎样解决这个问题?
3.2.4 效果
我们来考虑经过使用 Composite模式后给系统的架构带来了那些效果:
z& &&&简化了 JUnit 的代码& &JUnit 可以统一处理组合结构TestSuite 和单个对象 TestCase。使 JUnit 开发变得简单容易,因为不需要区分部分和整体的区别,不需要写一些充斥着 if else 的选择语句。z& &&&定义了 TestCase 对象和 TestSuite 的类层次结构& &基本对象 TestCase 可以被组合成更复杂的组合对象 TestSuite,而这些组合对象又可以被组合,如上个例子,这样不断地递归下去。在程序的代码中,任何使用基本对象的地方都可方便的使用组合对象,大大简化系统维护和开发。z& &&&使得更容易增加新的类型的 TestCase,如下面介绍的 Decorate 模式来扩展 TestCase 的功 能
3.3& & Template Method(模板方法)
3.3.1 问题在实际的测试中,为了测试业务逻辑,必须构造一些参数或者一些资源,然后才可进行测 试,最后必须释放这些系统资源。如测试数据库应用时,必须创建数据库连接 Connection, 然后执行操作,最后必须释放数据库的连接等。如下代码:public void testUpdate(){// Load the Oracle JDBC driver and Connect to the database DriverManager.registerDriver(new oracle.jdbc.OracleDriver()); String url = &jdbc:oracle:thin:@localhost:1521:ORA91&;Connection conn = DriverManager.getConnection (url, &hr&, &hr&);// Select first_name and last_name column from the employees tableResultSet rset = stmt.executeQuery (&select FIRST_NAME, LAST_NAME from EMPLOYEES&);……………// Disconnect conn.close ();}
其实这种情况很多,如测试 EJB时,必须进行 JNDI 的 LookUp,获得 Home 接口等。可是 如果在一个 TestCase 中有几个测试方法,例如测试对数据库的 Insert,Update,Delete,Select 等操作,这些操作必须在每个方法中都首先获得数据库连接 connection,然后测试业务逻辑, 最后再释放连接。这样就增加了测试人员的工作,反复的书写这些代码,与 JUnit 当初的设 计目标不一致?怎样解决这个问题?
3.3.2 模式的选择
接下来要解决的问题是给开发者一个便捷的“地方”,用于放置他们的初始化代码,测试代码,和释放资源的代码,类似对象的构造函数,业务方法,析构函数一样。并且必须保证 每次运行测试代码之前,都运行初始化代码,最后运行释放资源代码,并且每一个测试的结 果都不会影响到其它的测试结果。这样就达到了代码的复用,提供了测试人员的效率。
Template Method(模板方法)可以比较好得解决这个的问题。摘引其意图,“定义一个 操作中算法的骨架,并将一些步骤延迟到子类中。Template Method使得子类能够不改变一个算法的结构即可重新定义该算法的某些特定步骤。”这完全恰当。这样可以使测试者能够 分别来考虑如何编写初始化和释放代码,以及如何编写测试代码。不管怎样,这种执行的次 序对于所有测试都将保持相同,而不管初始化代码如何编写,或测试代码如何编写。Template Method(模板方法)静态结构如下图所示
这里设计到两个角色,有如下责任
z& &&&AbstractClass&&定义多个抽象操作,以便让子类实现。并且实现一个具体的模板方法, 它给出了一个顶级逻辑骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类里实现。模板方法也有可能调用一些具体的方法。z& &&&ConcreteClass&&实现父类 的抽象操作 方法,它们 是模板方法 的组成步骤 。&&每一个 AbstractClass 可能有多个ConcreteClass 与之对应,而每一个 ConcreteClass 分别实现抽 象操作,从而使得顶级逻辑的实现各不相同。
<font color="#.3.3 实现
于是我们首先把 TestCase 分成几个方法,哪些是抽象操作以便让开发人员去实现,哪个是
file:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image001.giffile:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif具体的模板方法,现在我们来看 TestCase 源码
public abstract class TestCase extends Assert implements Test {//定义抽象操作,以便让子类实现protected void setUp() throws Exception {}protected void runTest() throws Throwable {}protected void tearDown() throws Exception {}//具体的模板方法,定义出逻辑骨架public void runBare() throws Throwable {setUp(); runTest(); tearDown();}}
file:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image001.giffile:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image001.gifsetUp 方法让测试人员实现,去初始化测试信息,如数据库的连接, EJBHome 接口的 JNDI 等,而 tearDown 方法则是实现测试完成后的资源释放等清除操作。runTest 方法则是开发 人员实现的测试业务逻辑。最后 TestCase 的方法 runBare则是模板方法,它实现了测试的 逻辑骨架,而测试逻辑的组成步骤 setUp, runTest, teardown,推迟到具体的子类实现,如一 个具体的测试类public class TestHelloWorldTestClientJUnit1 extends TestCase {public void setUp() throws Exception {initialize();//初始化 JNDI 信息create();& && &&&//获得 EJB 的 Home 接口,和远程接口}public void testGetMessage() throws RemoteException {assertNotNull(ERROR_NULL_REMOTE, helloWorld);this.assertEquals(&Hello World&,helloWorld.getMessage(&&));//测试业务逻辑}public void tearDown() throws Exception { helloWorldHome = //释放 EJB 的 Home 接口 helloWorld =& && &&&//释放 EJB 的远程接口}}
子类实现了 setUp,tearDown&&方法,和一个测试方法 testGetMessage,为什么名称不是 runTest,这就是在下面介绍Adapter模式的原因,它把 testGetMessage 方法适配成 runTest。 下面是类的关系图:
<font color="#.3.4 效果我们来考虑经过使用 Template Method 模式后给系统的架构带来了那些效果:z& &&&在各个测试用例中的公共的行为(初始化信息和释放资源等)被提取出来,可以避免代码的重复,简化了测试人员的工作。z& &&&在 TestCase 中实现一个算法的不变部分,并且将可变的行为留给子类来实现。增强了 系统的灵活性。使 JUnit 框架仅负责算法的轮廓和骨架,而测试的开发人员则负责给出 这个算法的各个逻辑步骤。<font color="#.4& & Adapter(适配器)<font color="#.4.1 问题我们已经应用了 Command 模式来表现一个测试用例。Command 依赖于一个单独的像execute()这样的方法(在 TestCase 中称为 run())来对其进行调用。这样允许我们能够通过 相同的接口来调用一个 command 的不同实现。如果实现一个测试用例,就必须实现继承Testcase,然后实现 run 方法,实际是 testRun, 测试人员就把所有的测试用例都继承相同的类,这样的结果就会造成产生出大量的子类,使 系统的测试维护相当困难,并且 setUp 和 tearDown 仅为这个 testRun 服务,其他的测试也必 须完成相应的代码,从而增加了开发人员的工作量,怎样解决这个问题?为了避免类的急剧扩散,试想一个给定的测试用例类可以实现许多不同的方法,每一个方 法都有一个描述性的名称,如 testGetMessage 或 testSetMessage。这样测试用例并不符合简 单的 command 接口。因此又会带来另外一个问题就是,使所有测试方法从测试调用者的角 度(JUnit 框架)上看都是相同的。怎样解决这个接口不匹配问题?<font color="#.4.2 模式的选择思考设计模式的适用性,Adapter(适配器)模式便映入脑海。Adapter 具有以下意图“将一个类的接口转换成客户希望的另外一个接口”。这听起来非常适合。把具有一定规则的描 述性方法如 testGetMessage,转化为 JUnit 框架所期望的 Command(TestCase 的 run)从而 方便框架执行测试。Adapter模式又分为类适配器和对象适配器。类适配器是静态的实现在 这里不适合使用,于是使用了对象适配器。
对象的适配器模式的结构如下图所示:
这里涉及到三个角色,有如下责任z& &&&Target&&系统所期望的目标接口z& &&&Adaptee&&现有需要适配的接口z& &&&Adapter&&适配器角色,把源接口转化成目标接口<font color="#.4.3 实现file:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image001.giffile:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif在实现对象的适配时,首先在 TestCase 中定义测试方法的命名规则必须是 publicvoid testXXXXX()这样我们解析方法的名称,如果符合规则认为是测试方法,然后使用 Adapter 模式把这些方法,适配成 Command 的 runTest 方法。在实现时使用了 java的反射技术,这样便可很容易实现动态适配。代码如下
&font color=&#000000&&protected void runTest() throws Throwable {//使用名称获得对象的方法,如 testGetMessage,然后动态调用,适配成 runTest 方法Method runMethod= getClass().getMethod(fName, null);runMethod.invoke(this, new Class[0]);}&/font&
在这里目标接口 Target 和适配器 Adapter 变成了同一个类,TestCase,而测试用例,作为Adaptee,其结构图如下:
<font color="#.4.4 效果我们来考虑经过使用 Adapter模式后给系统的架构带来了那些效果:z& &&&使用 Adapter 模式简化测试用例的开发,通过按照方法命名的规范来开发测试用例,不 需要进行大量的类继承,提高代码的复用,减轻测试人员的工作量z& &&&使用 Adapter可以重新定义 Adaptee 的部分行为,如增强异常处理等<font color="#.5& & Observer(观察者)<font color="#.5.1 问题如果测试总是能够正确运行,那么我们将没有必要编写它们。只有当测试失败时测试才是有意义的,尤其是当我们没有预期到它们会失败的时候。更有甚者,测试能够以我们所预期的方式失败。JUnit 区分了失败(failures)和错误(errors)。失败的可能性是可预期的,
并且以使 用断言( assertion )来 进行检查 。而错误 则是不可 预期的问 题,如ArrayIndexOutOfBoundsException。因此我们必须进行报告测试的进行状况,或者打印到控制台,或者是文件,或者 GUI 界面,甚至同时需要输出到多种介质。如 JUnit 提供了三种方 式如 Text,AWT,Swing 这三种运行方式,并且JUnit 需要提供方便的扩展接口,这样就存在 对象间的依赖关系,当测试进行时的状态发生时(TestCase 的执行有错误或者失败等),所有依赖这些状态的对象必须自动更新,但是 JUnit 又不希望为了维护一致性而使各个类紧密耦合,因为这样会降低它们的重用性,怎样解却这个问题?<font color="#.5.2 模式的选择同样需要思考设计模式的适用性,Observer(观察者)模式便是第一个要考虑的。Observer观察者模 式是行为 模式,又 叫做发布 -订阅 (Publish-Subscribe) 模式,模型-视图(Model/View)模式,源-监听器(Source/Listener)模式。具有以下意图“定义对象间的 一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并 被自动更新”。这听起来非常适合需求。在 JUnit 测试用例时,测试信息一旦发生改变,如发 生错误或者失败,结束测试等,各种输出就要有相应的更新,如文本输出就要在控制台打印 信息,GUI 则在图形中标记错误信息等。Observer(观察者)模式的结构如下图所示:Observer(观察者)模式的角色z Subject& & 提供注册和删除观察者对象的方法,可以保存多个观察者z& &&&ConcreteSubject 当它的状态发生改变时,向它的各个观察者发出通知 z& &&&Observer&&定义那些目标发生改变时需要获得通知的对象一个更新接口 z& && & ConcreteObserver&&实现更新接口3.5.3 实现file:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image001.giffile:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif首先定义 Observer 观察者的就是 TestListener,它是一个接口,定义了几个方法,说明它监 听的几个方法。如测试开始,发生失败,发生错误,测试结束等监听事件的时间点。由具体的类来实现
// A Listener for test progress public interface TestListener {// An error occurred.public void addError(Test test, Throwable t);// A failure occurred.public void addFailure(Test test, AssertionFailedError t);// A test started.public void startTest(Test test);//A test ended.public void endTest(Test test);}
JUnit 里有三种方式来实现 TestListener,如 TextUI,AWTUi,SwingUI 并且很容易使开发人 员进行扩展,只需实现 TestListener 即可。下面看在 TextUi 方式是如何实现的,它由一个类ResultPrinter 实现。public class ResultPrinter implements TestListener { PrintStream fW& && &&&* A test ended.public PrintStream getWriter() {return fW}public void startTest(Test test) {getWriter().print(&.&);}public void addError(Test test, Throwable t) {getWriter().print(&E&);}public void addFailure(Test test, AssertionFailedError t) {getWriter().print(&F&);}public void endTest(Test test) {}}
JUnit 中使用 TestResult 来收集测试的结果,它使用 Collecting&&Parameter(收集参数)设 计模式(TheSmalltalk Best Practice Patterns 中有介绍),它实际是 ConcreteSubject,在 JUnit 中 Subject 和 ConcreteSubject 是同一个类,我们看它的实现
public class TestResult extends Object {
//使用 Vector 来保存,事件的监听者
protected Vector fListeners = new Vector();
// Registers a TestListener
public synchronized void addListener(TestListener listener) {
fListeners.addElement(listener);
//Unregisters a TestListener
public synchronized void removeListener(TestListener listener) {
fListeners.removeElement(listener);
//Informs the result that a test will be started. public void startTest(Test test) {
for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
((TestListener)e.nextElement()).startTest(test);
//Adds an error to the list of errors. The passed in exception
//caused the error.
public synchronized void addError(Test test, Throwable t) {
for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) { ((TestListener)e.nextElement()).addError(test, t);
//以下省略了 addFailure 和 endTest 代码
我们来查看他们的类图关系。<font color="#.5.4 效果我们来考虑经过使用 Observer模式后给系统的架构带来了那些效果:z& &&&Subject 和 Observer 之间地抽象耦合& &一个 TestResult 所知道的仅仅是它有一系列的观 察者,每个观察者都实现 TestListener 接口,TestResult 不必知道任何观察者属于哪一个 具体的实现类,这样使 TestResult 和观察者之间的耦合是抽象的和最小的。z& &&&支持广播通信& &被观察者 TestResult 会向所有的登记过的观察者如 ResultPrinter 发出通 知。这样不像通常的请求,通知的发送不需指定它的接收者,目标对象并不关心到底有 多少对象对自己感兴趣,它唯一的职责就是通知它的观察者。<font color="#.6& & Decorate(装饰)<font color="#.6.1 问题经过以上的分析知道 TestCase 是一个及其重要的类,它定义了测试步骤和测试的处理。
但是作为一个框架,应该提供很方便的方式进行扩展,进行二次开发。允许不同的开发人员开发适合自己的 TestCase,如希望 Testcase 可以多次反复执行, TestCase 可以处理多线程, TestCase 可以测试 Socket 等扩展功能。当然使用继承机制是增加功能的一种有效途径,例 如 RepeatedTest 继承 TestCase 实现多次测试用例,测试人员然后继承 RepeatedTest 来实现。 但是这种方法不够灵活,是静态的,因为每增一种功能就必须继承,使子类数目呈爆炸式的 增长,开发人员不能动态的控制对功能增加的方式和时机。JUnit 必须采用一种合理,动态 的方式进行扩展。<font color="#.6.2 模式的选择
同样需要思考设计模式的适用性,Decorator(装饰)模式是首先要考虑的。Decorator(装 饰)模式又名包装(Wrapper)模式。其意图是“动态地给一个对象添加一些额外的职责。就 增加功能来说,Decorator 模式相比生成子类更为灵活”。这完全符合我们的需求,可以动态 的为TestCase增加职责,或者可以动态地撤销,动态的任意组合。
Decorator(装饰)模式的结构如下图所示:Decorator(装饰)模式的角色如下:z& &&&Component&&给出抽象接口,以规范对象z& &&&ConcreteComponent&&定义一个将要接收附加责任的类z& &&&Decorator&&持有一个构件对象的实例,并且定义一个与抽象构件 Component 一致的接口z& &&&ConcreteDecorator& & 负责给构件对象附加职责3.6.3 实现file:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image001.giffile:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif明白了 Decorator 模式的结构后,其实 Test 接口便是 Component抽象构件角色。TestCase 便是 ConcreteComponent 具体构件角色。必须增加 Decorator 角色,于是开发 TestDecorator 类,它首先要实现接口 Test,然后有一个私有的属性 TestfTest,接口的实现 run 都委托给 fTest的 run,该方法将有 ConcreteComponent 具体的装饰类来实现,以增强功能。代码如下:
public class TestDecorator extends Assert implements Test {//将要装饰的类,给它增加功能protected Test fTpublic TestDecorator(Test test) {fTest=}public void run(TestResult result) {fTest.run(result);}}
虽然 Decoretor 类不是一个抽象类,在实际应用中也不一定是抽象类,但是由于它的功能是一个抽象角色,因此称它为抽象装饰。下面是一个具体的装饰类 RepeatedTest它可以多次执 行一个 TestCase,这增强了 TestCase 的职责public class RepeatedTest extends TestDecorator {private int fTimesRpublic RepeatedTest(Test test, int repeat) {super(test);fTimesRepeat=}//为 ConcreteComponent 增加功能,可以执行多次public void run(TestResult result) {for (int i= 0; i & fTimesR i++) {if (result.shouldStop())super.run(result); //委托父类完成}}}然后可以看出这几个类的关系如图:
于是我们就可以动态的为 TestCase 增加功能,如下
public static Test suite() {TestSuite suite = new TestSuite();suite.addTest(new TestSetup(new RepeatedTest(new Testmath(&testAdd&),12)));}于是可以动态的实现功能的增加,首先使用一个具体的 TestCase,然后通过 RepeatedTest给这个 TestCase 增加功能,可以进行多次测试,然后又通过TestSetup 装饰类再次增加功能。
3.6.4 效果我们来考虑经过使用 Decorator 模式后给系统的架构带来了那些效果:z& &&&实现了比静态继承更加灵活的方式,动态的增加功能。& &如果希望给 TestCase 增加功 能如多次测试,则不需要直接继承,而只需使用装饰类 RepeatedTest 如下即可 suite.addTest (new RepeatedTest(new Testmath(&testAdd&),12)).这样便方便的为一个 TestCase 增加功能。 file:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image001.giffile:///C:/Users/Fayedme/AppData/Local/Temp/msohtmlclip1/01/clip_image001.gifz& &&&避免在层次结构中的高层的类有太多的特征 Decorator 模式提供了一种“即用即付”的 方式来增加职责,它不使用类的多层次继承来实现功能的累积,而是从简单的TestCase 组合出复杂的功能,如下增加了两种功能,而不用两层继承来实现。suite.addTest(new TestSetup(new RepeatedTest(new Testmath(&testAdd&),12)));这样开发人员不必为不需要的功能付出代价。3.7& & 总结最后,让我们作一个简单的总结,由于在 JUnit 中使用了大量的模式,增强框架的灵活性, 方便性,易扩展性。本人对 Junit 进行了代码简化(仅 13 个类),保留了它的精华(架构和 模式),可供大家学习。
本帖最后由 yuke198907 于
14:21 编辑
助理工程师
源代码链接 分享下:
助理工程师
引用:原帖由 yuke198907 于
13:54 发表
嗯,不错,junit测试还是会经常用到的 通过这测试框架可以多研究点东西,很有用。
本帖最后由 娟儿pop 于
17:18 编辑
谢谢啦~正好用到~

我要回帖

更多关于 testwriter 的文章

 

随机推荐