Java Annotation原理分析(五) --- JUnit案例分析

原创 2014年02月07日 19:27:32

 引言:  通过之前的内容,大家基本上对Java Annotation已经有了一个深入的了解,在本节,我们将基于JUnit项目的源代码,来分析其Annotation的使用和实现机制。 

 

 8.1  JUnit

      JUnit是开源领域鼎鼎大名的单元测试框架,从4.x开始,基于Java新的Annotation机制,抛弃了基于继承的单元测试开发方式,转向了基于Annotation标注的单元测试开发。基于Annotation解耦,简单,而且语义也很强大。

     主要的标注有: @Test, @Ignore, @Before, @After, @BeforeClass, @AfterClass, @RunWith, @Theory, @DataPoint等等,这里不再一一列出。

     这里简要介绍一些主要的若干标签功能描述:

        @Test:  用以标注,当前这个方法需要运行,做为test case.

        @Before/@After:  在每个测试方法运行之前/之后,需要执行

        @BeforeClass/@AfterClass:  在测试类中的测试方法执行之前/之后,只执行一次。

    我们将在后续的内容,重点分析这几个Annotation的抽象和执行过程。

 8.2  JUnit中Annotation的模型

        在JUnit处理Annotation的过程中,首先需要将代码中的Annotation提取出来,并存放到相应的数据结构里。 具体请看如下的示意图:

       

      TestClass:   来查看一下代码中的注释,Wraps a class to be run, providing method validation and annotation searching。封装了一个将要运行的Class,并存储其中的Annotation,以备验证或者查询之用。

     在TestClass.java之中定义以下两个数据结构,来存放方法和Field的Annotation信息:

    private Map<Class<?>, List<FrameworkMethod>> fMethodsForAnnotations = new HashMap<Class<?>, List<FrameworkMethod>>();

    private Map<Class<?>, List<FrameworkField>> fFieldsForAnnotations = new HashMap<Class<?>, List<FrameworkField>>();

     FrameworkMethod:  代表在测试类中的一个方法。               

     FrameworkField:     代表在测试类中的一个Field.

     FrameworkMember: 定义了其中的共性信息。

    那他们是在什么位置被调用的呢?下面我们来简单过一下这个过程。

       

                资料来源:    参阅参考文档1.

 

        在步骤7,8的时候,会创建适当的Runner。这里的Runner只是一个抽象类。具体的调用步骤如下:

        JUnitCore.runrun(Class<?>... classes)  ---> Request.classes(defaultComputer(), classes) 

           --> new AllDefaultPossibilitiesBuilder() instance, 创建Runner,返回Runner.

             --> 如果使用Annotation,则会返回JUnit4Builder实例,参见AllDefaultPossibilitiesBuilder.runForClass().

              -->JUnit4Builder唯一的覆盖方法,runForClass()返回BlockJUnit4ClassRunner()实例。

              -->  在调用ClassRunner实例之时,会调用在其父类ParentRunner构造函数中的创建TestClass对象。

         真正的调用过程,其实就是发生在ParentRunner的构造函数中。  

public abstract class ParentRunner<T> extends Runner implements Filterable,
        Sortable {
   ..........
/**
     * Constructs a new {@code ParentRunner} that will run {@code @TestClass}
     */
    protected ParentRunner(Class<?> testClass) throws InitializationError {
      fTestClass = new TestClass(testClass); //调用位置
        validate();
    }
    ........
}

 8.3  JUnit中Annotation的执行过程

      在这一节我们将来看看,在JUnit框架中, TestCase是如何被执行的。首先上图,有一个整体的观感。

     

       首先来看一下执行的抽象实体:Statement;  Represents one or more actions to be taken at runtime in the course of running a JUnit test suite. 就是说,在执行过程中,执行Test的行为的实体。

public abstract class Statement {
    /**
     * Run the action, throwing a {@code Throwable} if anything goes wrong.
     */
    public abstract void evaluate() throws Throwable; //执行方法
}

  evaluate()是具体的TestCase的执行主体,分别于InvokeMethod,RunBefores,RunAfters具体实现类。  

  

    InvokeMethod: 具体执行单个的TestCase。

public class InvokeMethod extends Statement {
    private final FrameworkMethod fTestMethod;
    private Object fTarget;

    public InvokeMethod(FrameworkMethod testMethod, Object target) {
        fTestMethod = testMethod;
        fTarget = target;
    }

    @Override  //覆盖evaluate方法
    public void evaluate() throws Throwable {
       fTestMethod.invokeExplosively(fTarget);   //执行当前的TestCase
    }
}

    RunBefores/RunAfters:  执行一系列的Before/After标注的方法;其中,基于还有指向下一个Statement的引用,用以表示在当前的方法逻辑执行完毕之后,接下来要执行的方法;这里Before/Afters各有不同,这里仅仅列出Before的代码示例。

public class RunBefores extends Statement {
    private final Statement fNext; //测试方法

    private final Object fTarget;  //目标对象

    private final List<FrameworkMethod> fBefores; //before方法引用

    public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) {
        fNext = next;
        fBefores = befores;
        fTarget = target;
    }

    @Override  //覆盖evaluate方法
    public void evaluate() throws Throwable {
        for (FrameworkMethod before : fBefores) {
            before.invokeExplosively(fTarget); //优先执行before方法
        }
        fNext.evaluate(); //执行后续方法
    }
}
   在抽象类Runner中的run()方法是执行的主要入口方法,在ParentRunner中的runChild()/getChildren()方法是抽象方法,他们在Block4JUnitClassRunner中加以实现。

   ParentRunner.run() --> ParentRunner.classBlock() -->ParentRunner.childrenInvoker() --> ParentRunner.runChildren()

                 ----> Block4JUnitClassRunner.runChild().

     诸位可以发现,主要执行过程由Runner来完成,具体的单个方法执行于Statement来执行。

 8.4  题外话

     JUnit框架设计是非常精妙的,其中应用了诸多的设计模式,具体可以参看8.6中的参考资料,值得花点时间看看。 那为什么系统会设计成这个样子,将系统进行这样的抽象和设计,也是我在研究的时候,一直在思考的内容。我会在接下来的另外一篇文章中去专门讨论JUnit的系统设计思路。

 8.5  总结

    在JUnit中Annotation仅仅是做一个anchor,存在运行状态中;在运行过程中,将这些Annotation提取到相应的数据结构中,然后根据这些数据结构,执行响应的操作。综合而言,在我们将来的系统设计中,如果要应用Annotation,更多的是起到anchor的作用,在运行中,作为执行的标识。

     对于框架而言,在诸多功能中使用Annotation,可以极大简化框架使用者的工作量,例如Spring,Hibernate以及JUnit中Annotation的使用,摆脱对于继承的依赖。对于Annotation来说,主要的工作复杂度就在于框架实现代码,需要处理这些Annotation的解析过程,它们由框架设计者来完成。

8.6 参考文档

1.  http://my.oschina.net/pangyangyang/blog/153320

 2. www.junit.org

相关文章推荐

JUnit中标注Annotation介绍

java标注(Annotation) 对于Annotation,是Java5的新特性,JDK5引入了Metedata(元数据)很容易的就能够调用Annotations。Annotations提供一些本...

JUnit框架功能详细——JUnit学习(一)

目录[-] 一个简单的JUnit JUnit的生命周期 使用@RunWith注解 未完待续 是著名的单元测试框架,在JUnit4中所有的测试用例采用@Annotati...
  • ggj20ss
  • ggj20ss
  • 2015年07月15日 18:01
  • 907

浅谈Spring测试框架+junit4单元测试原理

Spring的主要测试框架核心: 3个接口 1TestContext:负责持有一个当前测试的上下文 2TestContextManger: (1)每次启动都会创建,管理一个TestContex...

Spring 使用Junit的MockMvc 写测试用例

怕以后忘掉,随便写写 1、spring配置文件 该声明为bean的类就声明,测试之前项目要能运行,所以spring的配置文件问题就不多说了,下面的数据库配置和测试类中负责回滚的Transactio...

Java类加载及创建实例(new)顺序

Java类加载 1.JVM( Java Virtual Machine,“Java虚拟机” )的类装载器ClassLoader(java类),包括系统类、扩展类、用户编写三种类加载器,负责将java的...

Java Annotation原理分析(一)

小引: 在当下的Java语言层面上,Annotation已经被应用到了语言的各个方面,它已经在现在的ssh开发中,通过Annotation极大的提高了开发的效率,堪称开发神器。在这篇文章中,我们来了解...

Java Annotation原理分析(四) -- 实现原理分析

在我们了解完了Annotation诸多的基础知识之后,不禁会想,Annotation是如何在代码和系统中被处理和执行的?...

Java之--------JUnit软件测试技术(工具)Annotation1

编程世界很大,我们确实很微妙,所以我们需要聚少成多,慢慢的强化自己

Spring2.5源码解读 之 基于annotation的Controller实现原理分析(1)

网上已有许多关于Spring源码解读的文章,但对于SpringMVC中基于annotation的Controller这一块,目前还没发现有相关源码解读的文章,这几天,一直在研究SpringMVC,小有...

Java Annotation原理分析(三) --- 内置Annotation介绍

总结和描述Java中Annotation的用法。
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java Annotation原理分析(五) --- JUnit案例分析
举报原因:
原因补充:

(最多只允许输入30个字)