`
kidiaoer
  • 浏览: 822311 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

junit源码分析

阅读更多
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;
    /**
   

//具体命令二

public class TestSuite implements Test {
    
由此带来的好处:
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源码分析(二)——观察者模式 - 庄周梦蝶时间:2008-10-21 23:33来源:信息化中国
作者: 点击:7次 收藏 挑错 推荐 打印
我们知道JUnit支持不同的使用方式:swt、swing的UI方式,甚至控制台方式,那么对于
这些不同的UI我们如何提供统一的接口供它们获取测试过程的信息(比如出 ...
  
我们知道JUnit支持不同的使用方式:swt、swing的UI方式,甚至控制台方式,那么对于
这些不同的UI我们如何提供统一的接口供它们获取测试过程的信息(比如出现的异常信息,
测试成功,测试失败的代码行数等等)?我们试想一下这个场景,当一个error或者exception产
生的时候,测试能够马上通知这些UI客户端:发生错误了,发生了什么错误,错误是什么等等。
显而易见,这是一个订阅-发布机制应用的场景,应当使用观察者模式。那么什么是观察者模式呢?

观察者模式(Observer)

Observer是对象行为型模式之一

1.意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发现改变时,所有依赖于它
的对象都得到通知并被自动更新

2.适用场景:
1)当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,通过观察者模式将这两者
封装在不同的独立对象当中,以使它们可以独立的变化和复用
2)当一个对象改变时,需要同时改变其他对象,并且不知道其他对象的具体数目
3)当一个对象需要引用其他对象,但是你又不想让这个对象与其他对象产生紧耦合的时候

3.UML图:

Subject及其子类维护一个观察者列表,当需要通知所有的Observer对象时调用Nitify方法
遍历Observer集合,并调用它们的update方法更新。而具体的观察者实现Observer接口
(或者抽象类),提供具体的更新行为。其实看这张图,与Bridge有几分相似,当然两
者的意图和适用场景不同。

4.效果:
1)目标和观察者的抽象耦合,目标仅仅与抽象层次的简单接口Observer松耦合,而没
有与具体的观察者紧耦合
2)支持广播通信
3)缺点是可能导致意外的更新,因为一个观察者并不知道其他观察者,它的更新行为
也许将导致一连串不可预测的更新的行为

5.对于观察者实现需要注意的几个问题:
1)谁来触发更新?最好是由Subject通知观察者更新,而不是客户,因为客户可能忘记调用Notify
2)可以通过显式传参来指定感兴趣的更新
3)在发出通知前,确保Subject对象状态的一致性,也就是Notify操作应该在最后被调用
4)当Subject和Observer的依赖关系比较复杂的时候,可以通过一个更新管理器来管理
它们之间的关系,这是与中介者模式的结合应用。


讨论完观察者模式,那我们来看JUnit是怎么实现这个模式的。在junit.framework包中
我们看到了一个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, Throwable t);
/**
* A test ended.
*/
public void endTest(Test test);
/**
* A test started.
*/
public void startTest(Test test);
}
接口清晰易懂,就是一系列将测试过程的信息传递给观察者的操作。具体的子类
将接受这些信息,并按照它们的方式显示给用户。
比如,我们看看swing的UI中的TestRunner,它将这些信息显示在一个swing写的UI界面上:
showInfo("Running: " test);
}

public void addError(Test test, Throwable t) {
fNumberOfErrors.setText(Integer.toString(fTestResult.errorCount()));
appendFailure("Error", test, t);
}
public void addFailure(Test test, Throwable t) {
fNumberOfFailures.setText(Integer.toString(fTestResult.failureCount()));
appendFailure("Failure", test, t);
}
public void endTest(Test test) {
setLabelValue(fNumberOfRuns, fTestResult.runCount());
fProgressIndicator.step(fTestResult.wasSuccessful());
}

可以看到,它将错误信息,异常信息保存在List或者Vector集合内,然后显示在界面上:
int index= fFailureList.getSelectedIndex();
if (index == -1)
return;

Throwable t= (Throwable) fExceptions.elementAt(index);
if (fTraceFrame == null) {
fTraceFrame= new TraceFrame();
fTraceFrame.setLocation(100, 100);












JUnit源码分析 (三)——Template Method模式
    在JUnit执行测试时,我们经常需要初始化一些环境供测试代码使用,比如数据
库连接、mock对象等等,这些初始化代码应当在每一个测试之前执行并在测试方
法运行后清理。在JUnit里面就是相应的setUp和tearDown方法。如果没有这两个
方法,那么我们要在每个测试方法的代码内写上一大堆重复的初始化和清理代码,这
是多么愚蠢的做法。那么JUnit是怎么让setUp和tearDown在测试执行前后被调用的呢?
    如果你查看下TestCase方法,你会发现TestCase和TestSuite的run()方法都是将执行
测试的任务委托给了TestResult,由TestResult去执行测试代码并收集测试过程中的
信息(这里用到了Collecting Parameter模式)。
        public TestResult run() {
        TestResult result= createResult();
        run(result);
        return result;
    }
    /**
     * Runs the test case and collects the results in TestResult.
     * This is the template method that defines the control flow
     * for running a test case.
     */
    public void run(TestResult result) {
        result.run(this);
    }   
    我们直接找到TestResult,看看它的run方法:
/**
     * Runs a TestCase.
     */
    protected void run(final TestCase test) {
        startTest(test);
        Protectable p = new Protectable() {
            public void protect() throws Throwable {
                test.runBare();
            }
        };
        runProtected(test, p);
        endTest(test);
    }
    这里实例化了一个内部类,内部类实现了Protectable接口的 protect()方法,并执
行传入的TestCase的runBare()方法,显然,真正的测试代码在TestCase的runBare()方
法中,让我们来看下:


        //将被子类实现
    protected void setUp() throws Throwable {
    }
    //同上,将被具体的TestCase实现
    protected void tearDown() throws Throwable {
    }
     /**
     * 模板方法
     * Runs the bare test sequence.
     * @exception Throwable if any exception is thrown
     */
    public void runBare() throws Throwable {
        setUp();
        try {
            runTest();
        }
        finally {
            tearDown();
        }
    }

真相水落石出,对于每一个测试方法,都遵循这样的模板:setUp->执行测
试 runTest()->tearDown。这正是模板方式模式的一个应用例子。什么是template method模式呢?

Template Method模式

类行为模式的一种
1.意图:定义一个操作中的算法的骨架,而将一些延迟步骤到子类中。Template Method使得子
类可以不改变一个算法的结构即可重定义该算法的某些步骤。
2.适用场景:
1)一次性实现算法的不变部分(基本骨架),将可变的行为留给子类来完成
2)子类中的公共部分(比如JUnit中的初始化和清理)被抽取到一个公共父类中以避免代码重复。
3)控制了子类的扩展,这里其实也有类似回调函数的性质,具体步骤先在骨架中注册,在具体执行时被回调。

3.UML图和结构
   
  抽象父类定义了算法的基本骨架(模板方法),而不同的子类实现具体的算法步骤,客户
  端由此可以与算法的更改隔离。

4.效果:
1)模板方法是代码复用的基本技术,在类库中经常使用,可以减少大量的代码重复
2)通过隔离算法的不变和可变部分,增加了系统的灵活性,扩展算法的某些步骤将变的很容易。

    了解了Template Method模式之后,让我们回到JUnit的源码,看看runTest()方法,这里主要
应用的是java的反射技术,对于学习反射技术的有参考价值:
protected void runTest() throws Throwable {
        Method runMethod= null;
        try {
            runMethod= getClass().getDeclaredMethod(fName, new Class[0]);
        } catch (NoSuchMethodException e) {
            fail("Method \""+fName+"\" not found");
        }
        if (runMethod != null && !Modifier.isPublic(runMethod.getModifiers())) {
            fail("Method \""+fName+"\" should be public");
        }

        try {
            runMethod.invoke(this, new Class[0]);
        }
        catch (InvocationTargetException e) {
            e.fillInStackTrace();
            throw e.getTargetException();
        }
        catch (IllegalAccessException e) {
            e.fillInStackTrace();
            throw e;
        }
    }






分享到:
评论

相关推荐

    Junit源码分析(圣思园)

    **Junit源码分析(圣思园)** Junit是Java编程语言中最广泛使用的单元测试框架,它使得开发者能够方便地编写和运行可重复的、可靠的测试用例。本篇文章将深入探讨Junit的源码,揭示其内部工作原理,帮助我们更好地...

    自定义junit源码

    自定义JUnit源码是一个对Java开发人员非常有帮助的主题,特别是对于那些想要深入理解测试框架工作原理或希望根据自身需求定制测试工具的开发者。JUnit是一个广泛使用的单元测试框架,它简化了编写和运行针对Java代码...

    对基于Junit的测试代码自动化生成的研究

    4. Junit源码分析:深入到Junit的源码层面,解释其核心组件和测试执行流程,为自动生成测试代码提供理论基础。 5. 实现策略:描述如何设计和实现一个自动化测试代码生成的系统,可能包括解析源代码结构,识别测试点...

    junit4 单元测试源码

    【标题】"junit4 单元测试源码"涉及的是Java编程中单元测试的重要工具...通过分析和运行这些源码,学习者可以掌握单元测试的基本概念,了解如何编写有效的测试用例,以及如何利用Eclipse的集成环境进行测试驱动开发。

    junit in action 源码

    10. **持续集成**:JUnit源码也揭示了如何将测试集成到持续集成(CI)系统,如Jenkins、Travis CI等,确保每次代码变更后都能自动运行测试并获取反馈。 总的来说,分析《JUnit in Action》的源码,不仅可以帮助我们...

    feed4junit源码

    它能够从业务分析人员定义好的CVS或 Excel文件读取测试用例数据并在构建/单元测试框架中报告测试成功。利用Feed4JUnit能够很方便用随机但校验过的数据执行冒烟测试来提高代码 代码覆盖率和发现由非常特殊的数据结构...

    Junit设计模式分析(带源码)

    本资源"Junit设计模式分析(带源码)"旨在深入探讨JUnit在设计上的模式和最佳实践,通过源码分析帮助开发者更好地理解和应用这个工具。 1. 单元测试基础: 单元测试是对软件中的最小可测试单元进行检查,如函数、...

    netbeans junit 源码

    2. **JUnit框架**:JUnit源码在NetBeans中的实现意味着我们可以看到如何在IDE内部封装和扩展JUnit的API。这可能涉及到对`@Test`注解的处理,以及如何触发测试执行和显示测试结果。 3. **事件监听和API调用**:...

    junit4测试源码

    通过分析和运行这些测试用例,我们可以了解如何编写有效的JUnit4测试,以及如何利用JUnit4提供的各种工具和特性来提高测试覆盖率和质量。 总之,理解和掌握JUnit4的源码对于Java开发者来说是至关重要的,它能帮助...

    javajunit源码-ant-java-junit:我的页面上的Java单元测试JUnit的源代码:http://jumpstartprog

    Java JUnit 源码分析 Java JUnit 是一个广泛使用的单元测试框架,它使得 Java 开发者能够方便地编写和执行针对代码功能的测试。JUnit 的核心在于它提供了断言(assertions)来验证代码行为,以及测试套件(test ...

    junit4 jar包以及源码

    同时,源码分析也有助于学习最佳实践和设计模式,提升自身的编程技能。 JUnit4的主要特性包括: 1. **注解驱动**:通过注解可以轻松地标识测试方法,如@Test表示测试方法,@Before和@After分别用于在每个测试方法...

    Springboot-junit项目源码

    SpringBoot-junit项目源码分析 SpringBoot是一个流行的Java框架,用于快速开发微服务和Web应用程序。它简化了Spring框架的配置,使得开发者可以更快地启动项目。JUnit是Java编程语言中最常用的单元测试框架,它使得...

    JUnit设计模式分析

    本篇将深入分析JUnit源码中的设计模式,帮助你理解其内在的架构原理,提升你的编程技能。 首先,JUnit的核心设计原则之一是“开闭原则”(Open-Closed Principle),它主张软件实体(类、模块、函数等)应对于扩展...

    Junit源码【肯特版】好像是4.几版忘了

    通过阅读和分析肯特参与编写的这部分源代码,我们可以更深入地了解JUnit的内部工作原理,学习如何设计和实现测试框架,以及如何通过注解和运行器优化测试流程。这对于理解单元测试的底层机制,提升测试效率,甚至...

    javajunit源码-java-junit-jenkins:使用Jenkins的CI的源代码-在我的页面上发布:http://jumpsta

    JavaJUnit Jenkins 源码分析 在 Java 开发过程中,单元测试是保证代码质量的重要环节,JUnit 是一个广泛使用的 Java 单元测试框架。而 Jenkins 是一个流行的持续集成(CI)工具,它可以帮助开发者自动化构建、测试...

    北京圣思园 junit全套笔记及源码

    【JUnit 框架详解】 JUnit 是一个广泛使用的 Java 编程语言的单元测试框架,由 Erich Gamma 和 Kent Beck 开发,它是 xUnit 家族的...结合源码分析,将帮助你更好地理解和应用这些概念,提升你的编程技能和代码质量。

    eclipse下利用ant、junit进行自动化测试例子源码

    6. **源码分析**:压缩包中的"testAntJunit"文件可能是包含了一个示例项目,这个项目演示了如何在Eclipse中设置ANT脚本和JUnit测试。通过查看源码,我们可以学习如何在代码中编写测试用例,以及如何在ANT构建文件中...

    JUnit设计模式分析及简化的JUnit代码

    JUnit是Java开发者进行单元测试的重要工具,由著名程序员Erich Gamma和Kent Beck共同创建,它遵循简洁、可扩展的原则,使得测试代码易于编写和维护。本文将深入探讨JUnit中的设计模式,以及如何通过理解这些模式来...

    JUnit -- 分析

    9. **源码分析**:可能深入到JUnit的源代码,解释其设计模式,如观察者模式、装饰器模式,以及JUnit如何处理测试失败和测试报告。 10. **实战示例**:结合实际项目,展示如何利用JUnit进行单元测试,解决实际问题。...

Global site tag (gtag.js) - Google Analytics