今天看了下Junit3的源码,记录下一些心得。
Junit的设计使用了许多设计模式。相关的文章网上也有很多。在TestResult中使用了Collecting Parameters 模式。这个模式的意义在于,该类的有几个成员变量集合,是负责记录测试的Failures和Erros,当然还有总共的Test的数目。
TestResult的run TestCase方法会调用具体的TestCase的run方法,如果失败(Failure 或 Error),将通过TestCase生成具体的是败类(TestFailure 或 TestError),最后在TestResult中,集合成员变量把相应的错误再收集起来。
这个模式可能比较简单,所以并没有在大话设计模式中看到过。
Junit3的基本结构可以用下图来表示(网上搜的):
这个图有些地方也没有画清楚,Test是个Interface,TestCase 继承了Assert并实现了Test, TestCase同时还引用了TestResult,TestCase的run方法,使用TestResult作为参数,其实调用了TestResult的run方法。 TestResult的run方法通过Test接口又回调了TestCase的runBare方法(包括setUp() 和 tearDown()的运行方法)。
再说说TestSuite。TestSuite也实现了Test接口,TestCase可以包括多个Test方法,而TestSuite可以包括多个TestCase,所以TestSuit也可以包含多个Test方法(此处说的Test方法是TestCase里的具体test的方法,不是Test类)。
所以TestSuite的使用方法有三种:
1)指定具体的TestCase对象。
2)指定具体的TestCase类(所有public的 void,无参数的 test开头的函数会被执行)
3)指定一个类组,包含多个TestCase类。
// 1) TestSuite suite= new TestSuite(); suite.addTest(new MathTest("testAdd")); suite.addTest(new MathTest("testDivideByZero")); // 2) TestSuite suite= new TestSuite(MathTest.class); // 3) Class[] testClasses = { MathTest.class, AnotherTest.class } TestSuite suite= new TestSuite(testClasses);
test方法的特点:
在TestSuite中又个isTestMethod()方法,说明了test方法的特点:
1)参数为零个
2)以test开头
3)void返回
正是这个特点也使Junit跟testNG比,还需要改进些。
设计模式总结:
1.命令模式。
Client发出请求,通过Command接口,具体的实现有Executor1,Executor2....ExecutorN,从而实现了Client到ExecutorN的解耦。
在Junit中,Test是个顶层的接口,TestCase是这个接口的抽象实现。具体的实现由Client决定,你可以写自己的testcase,extends这个TestCase,然后又自己的测试方法,test开头(这是适配器模式)。
在Junit中Client是Junit框架,例如它通过TestSuite来执行一系列的TestCase时,只要Client实现了TestCase接口,那么Junit框架就能够通过命令模式来调用具体的TestCase了。测试人员也不必关系Junit框架里面是怎么调用的。
2. 适配器模式。
上面也提到了适配器模式。TestCase是Test的抽象实现类。如果Client实现的TestCase只能执行TestCase中的test方法,那么会产生大量的客户实现类。
一般情况,当Client希望通过发送Request1,Request2,Request3,... RequestN来调用服务器的同一个方法,那么此时服务器端就需要一个Adapter来讲这些RequestN指向同一个具体的实现Adaptee。
在Junit中,框架的执行方法是run(),但是Client具体实现的TestCase可以是以test开头的public,void的方法,方法名随便定义。
3. 组合模式。
TestSuite可以灵活的组合不同的TestCase,包括三种方式。
4. 模板方法模式。
测试的流程具有可复制性。
5. 观察者模式。
JUnit提供了三种方式如Text,AWT,Swing这三种运行方式,并且JUnit需要提供方便的扩展接口,这样就存在对象间的依赖关系,当测试进行时的状态发生时(TestCase的执行有错误或者失败等),所有依赖这些状态的对象必须自动更新。
拿Text运行方式来说。在Junit的textUI包下,有两个类。ResultPrinter和TestRunner类,TestRunner的dorun方法,将resultPrinter加到TestResult的Listener列表中。 当TestCase运行时,会调用TestResult的run方法,TestResult方法的run 又调用不同的Listener的startTest方法,结束时调用endTest方法,出错时,addFailure和addError。就这样实现了观察者动态变化的模型。
6. 收集参数模式。
在TestResult中的run方法,参数是TestCase本身。定义了一个用来抛异常的Protectable对象,这个对象抛不同的exceptoin时,调用不同的add方法,添加Error列表和Failure列表。
7. 装饰模式
在Junit中可以对Test进行扩展,使测试人员能增加自己需要的特殊测试,如重复性测试RepeatTest,在多线程中测试ActiveTestSuite。
装饰模式又名包装(Wrapper)模式。其意图是“动态地给一个对象添加一些额外的职责”。 装饰模式有几个对象。装饰者,装饰的组件(component),具体的组件,装饰者的具体实现。
具体的组件实现了Componet,装饰者拥有组件的引用,用引用来实现这个组件的作用。当需要装饰不同的具体实现时,可以继承装饰者来实现自己的装饰。
在Junit中,Test就是那个Component,它有个具体的组件TestCase,一般的装饰者都是使用TestCase来实现测试。当你需要有自己的特殊需要时,你可能需哟啊一个TestDecorator(Junit帮你实现了)。TestDecorator中,它引用了Test这个组件。例如当你需要重复的测试,你可以写一个RepeatTest来继承TestDecorator,然后传入一个特殊的计数的变量,然后就可是实现自己的功能了。RepeatTest已经被Junit实现了。