JUnit源码分析(一)——Command模式和Composite模式

简介:
JUnit的源码相比于spring和hibernate来说比较简单,但麻雀虽小,五脏俱全,其中用到了比较多的设计模式。很多人已经在网上分享了他们对JUnit源码解读心得,我这篇小文谈不出什么新意,本来不打算写,可最近工作上暂时无事可做,那就写写吧,结合《设计模式》来看看。
    我读的是JUnit3.0的源码,目前JUnit已经发布到4.0版本了,尽管有比较大的改进,但基本的骨架不变,读3.0是为了抓住重点,省去对旁支末节的关注。我们来看看JUnit的核心代码,也就是Junit.framework包,除了4个辅助类(Assert,AssertFailedError,Protectable,TestFailure),剩下的就是我们需要重点关注的了。我先展示一张UML类图:

    我们先不去关注TestDecorator类(此处是Decorator模式,下篇文章再讲),看看Test接口,以及它的两个实现类TestCase和TestSuite。很明显,此处用到了Command模式,为什么要使用这个模式呢?让我们先来看看什么是Command模式。

Command模式

Command模式是行为型模式之一

1.意图:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
2.适用场景:
1)抽象出待执行的动作以参数化对象,Command模式是回调函数的面向对象版本。回调函数,我想大家都明白,函数在某处注册,然后在稍后的某个时候被调用。
2)可以在不同的时刻指定、排列和执行请求。
3)支持修改日志,当系统崩溃时,这些修改可以被重做一遍。
4)通过Command模式,你可以通过一个公共接口调用所有的事务,并且也易于添加新的事务。


3。UML图:
   

4.效果:
1)命令模式将调用操作的对象与如何实现该操作的对象解耦。
2)将命令当成一个头等对象,它们可以像一般对象那样进行操纵和扩展
3)可以将多个命令复合成一个命令,与Composite模式结合使用
4)增加新的命令很容易,隔离对现有类的影响
5)可以与备忘录模式配合,实现撤销功能。

    在了解了Command模式之后,那我们来看JUnit的源码,Test接口就是命令的抽象接口,而TestCase和TestSuite是具体的命令
// 抽象命令接口
package  junit.framework;

/**
 * A <em>Test</em> can be run and collect its results.
 *
 * 
@see  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.
     
*/
    
public   abstract   void  run(TestResult result);
}

// 具体命令一

public   abstract   class  TestCase  extends  Assert  implements  Test {
    
/**
     * the name of the test case
     
*/
    
private   final  String fName;
    
/**
    dot.gifdot.gif

//具体命令二

public class TestSuite implements Test {
     dot.gif

由此带来的好处:
1.客户无需使用任何条件语句去判断测试的类型,可以用统一的方式调用测试和测试套件,解除了客户与具体测试子类的耦合
2.如果要增加新的TestCase也很容易,实现Test接口即可,不会影响到其他类。
3.很明显,TestSuite是通过组合多个TestCase的复合命令,这里使用到了Composite模式(组合)
4.尽管未实现redo和undo操作,但将来也很容易加入并实现。

    我们上面说到TestSuite组合了多个TestCase,应用到了Composite模式,那什么是Composite模式呢?具体来了解下。

Composite模式

composite模式是对象结构型模式之一。
1.意图:将对象组合成树形结构以表示“部分——整体”的层次结构。使得用户对单个对象和组合结构的使用具有一致性。

2.适用场景:
1)想表示对象的部分-整体层次
2)希望用户能够统一地使用组合结构和单个对象。具体到JUnit源码,我们是希望用户能够统一地方式使用TestCase和TestSuite

3.UML图:

      

图中单个对象就是树叶(Leaf),而组合结构就是Compoiste,它维护了一个Leaf的集合。而Component是一个抽象角色,给出了共有接口和默认行为,也就是JUnit源码中的Test接口。

4.效果:
1)定义了基本对象和组合对象的类层次结构,通过递归可以产生更复杂的组合对象
2)简化了客户代码,客户可以使用一致的方式对待单个对象和组合结构
3)添加新的组件变的很容易。但这个会带来一个问题,你无法限制组件中的组件,只能靠运行时的检查来施加必要的约束条件

    具体到JUnit源码,单个对象就是TestCase,而复合结构就是TestSuite,Test是抽象角色只有一个run方法。TestSuite维护了一个TestCase对象的集合fTests:

      private  Vector fTests =   new  Vector( 10 ); 
      
/**
     * Adds a test to the suite.
     
*/
    
public   void  addTest(Test test) {
        fTests.addElement(test);
    }
    /**
     * Runs the tests and collects their result in a TestResult.
     
*/
    
public   void  run(TestResult result) {
        
for  (Enumeration e =  tests(); e.hasMoreElements(); ) {
              
if  (result.shouldStop() )
                  
break ;
            Test test
=  (Test)e.nextElement();
            test.run(result);
        }
    }


当执行run方法时遍历这个集合,调用里面每个TestCase对象的run()方法,从而执行测试。我们使用的时候仅仅需要把TestCase添加到集合内,然后用一致的方式(run方法)调用他们进行测试。

考虑使用Composite模式之后带来的好处:
1)JUnit可以统一地处理组合结构TestSuite和单个对象TestCase,避免了条件判断,并且可以递归产生更复杂的测试对象
2)很容易增加新的TestCase。


参考资料:《设计模式——可复用面向对象软件的基础》
          《JUnit设计模式分析》 刘兵
          JUnit源码和文档


文章转自庄周梦蝶  ,原文发布时间5.17

目录
相关文章
|
1月前
|
IDE Java 测试技术
Junit 单元测试
JUnit是Java常用的单元测试框架,简化了测试用例的编写和执行。其特点包括简单注解、自动化测试、可扩展性、灵活性及与IDE的集成。使用方法涉及创建测试类、利用注解如@Test、@BeforeEach等管理测试生命周期,以及使用各种断言方法验证结果。此外,JUnit支持参数化测试以覆盖更多输入组合,并能与Maven、Gradle等构建工具集成,提升测试效率和项目管理。
36 1
|
3月前
|
Java 测试技术
JUnit 4 单元测试
单元测试是软件开发中必不可少的一环,但是在平常开发中往往因为项目周期紧,工作量大而被选择忽略,这样往往导致软件问题层出不穷。 线上出现的不少问题其实在有单元测试的情况下就可以及时发现和处理,因此培养自己在日常开发中写单元测试的能力是很有必要的。无论是对自己的编码能力的提高,还是项目质量的提升,都是大有好处,本文将介绍 Java 单元测试框架 JUnit 4 的基础认识和使用来编写单元测试,希望同样对你有所帮助。
|
6月前
|
Java 测试技术 API
Junit5单元测试框架详解
前面我们学习了Selenium自动化测试框架,但是有的时候测试用例会很多,我们需要一个工具来管理这些测试用例,而Junit就是一个很好的管理工具,简单点来说,Junit就是一个针对Java的单元测试框架;
99 0
|
6月前
|
Java 测试技术 程序员
|
7月前
|
测试技术
Junit单元测试
Junit单元测试
37 0
|
7月前
|
Java 程序员 网络安全
JUnit5学习之六:参数化测试(Parameterized Tests)基础
了解JUnit5的参数化测试的基本知识
115 2
JUnit5学习之六:参数化测试(Parameterized Tests)基础
|
4月前
|
测试技术
Junit单元测试 org.junit.jupiter.api.extension.ParameterResolutionException异常处理
Junit单元测试 org.junit.jupiter.api.extension.ParameterResolutionException异常处理
84 0