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原理详解一

Interface:Test 整个测试的的基础接口 Method 1: abstract int countTestCases() 这个方法主要是用来计算要运行的test case的数量的。 Me...
  • yuebao1991
  • yuebao1991
  • 2015年12月30日 16:41
  • 2005

Spring+junit4 实现注解测试原理解析。

背景:   我们在使用Spring集成的项目进行开发的时候,需要在不依赖web容器启动的时候去进行单元测试,而Spring为我们提供了相应单元测试框架,在spring 的org.spring...
  • zhouzhiwengang
  • zhouzhiwengang
  • 2016年11月07日 17:46
  • 1509

Junit原理

JUnit是一个JAVA语言的单元测试框架。多数JAVA开发环境都wys
  • zjcheer_up
  • zjcheer_up
  • 2014年11月14日 13:33
  • 408

JUnit源码分析(一)

一、引子JUnit源码是我仔细阅读过的第一个开源项目源码。阅读高手写的代码能学到一些好的编程风格和实现思路,这是提高自己编程水平行之有效的方法,因此早就想看看这些赫赫有名的框架是怎么回事了。今天就拿最...
  • ai92
  • ai92
  • 2005年03月12日 22:11
  • 16829

基于 JUnit 单元测试的原理及示例

1 简介JUnit是一个Java语言的单元测试框架,它由 Kent Beck 和 Erich Gamma 建立,逐渐成为 xUnit 家族中最为成功的一个。 JUnit有它自己的JUnit扩展生态圈,...
  • qq_35246620
  • qq_35246620
  • 2017年01月19日 23:05
  • 2269

Junit4的使用和执行流程

Junit的使用和执行流程昨天晚上躺床上,在慕课网上看了一个关于讲解《Junit4》的视频教程。发现讲解的还不错,以前在写JAVA的过程中,自己也没有用到过Junit,而是通过类似的System.ou...
  • u010412719
  • u010412719
  • 2016年04月13日 10:19
  • 597

与JUnit干杯 ---- JUnit框架的架构原理以及应用

与JUnit干杯 ---- JUnit框架的架构原理以及应用安全合理而富有可扩展性与可读性的测试代码进入了对象中,它就象一棵大数那样支撑起了整个工程。1. PURPOSEu       理解系统测试与...
  • zrhk
  • zrhk
  • 2004年12月11日 22:27
  • 1838

Spring+junit4单元测试原理解析

背景:   我们在使用Spring集成的项目进行开发的时候,需要在不依赖web容器启动的时候去进行单元测试,而Spring为我们提供了相应单元测试框架,在spring 的org.springfram...
  • skyboy11yk
  • skyboy11yk
  • 2014年02月10日 19:39
  • 3635

junit4入门例子

第一步:eclipse中新建一个项目,java build path中导入junit jar包(本文例子junit-4.9b1.jar)第二步:新建一个package,导入如下四个文件第三步:右键Al...
  • zhuangxiu
  • zhuangxiu
  • 2011年03月17日 17:20
  • 6834

分析 JUnit 框架源代码

理解 JUnit 测试框架实现原理和设计模式 本文细致地描述了 JUnit 的代码实现,在展示代码流程 UML 图的基础上,详细分析 JUnit 的内部实现代码的功能与机制,并在涉及相关设计模式的...
  • gloria0610
  • gloria0610
  • 2014年03月15日 02:53
  • 1184
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java Annotation原理分析(五) --- JUnit案例分析
举报原因:
原因补充:

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