采用Junit4.8.2分析Junit实现架构

源码架构两个大包:junit包 org包

首先分析org.junit.runners.model包下的几个类

org.junit.runners.modela.TestClass

org.junit.runners.modela.FrameworkMethod

org.junit.runners.modela.FrameworkMember

org.junit.runners.modela.FrameworkField

涉及到的类:

org.junit.Assert;

org.junit.Before;

org.junit.BeforeClass;

org.junit.internal.runners.model.ReflectiveCallable;

org.junit.runners.BlockJUnit4ClassRunner;

package org.junit.runners.model;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass; /**
* Wraps a class to be run, providing method validation and annotation searching
*/
public class TestClass
{
// 封装的单元测试类的Class对象
private final Class<?> fClass;
// 注解类型->对应的方法List
private Map<Class<?>, List<FrameworkMethod>> fMethodsForAnnotations = new HashMap<Class<?>, List<FrameworkMethod>>();
// 注解类型->对应的属性List
private Map<Class<?>, List<FrameworkField>> fFieldsForAnnotations = new HashMap<Class<?>, List<FrameworkField>>(); //创建一个TestClass对象(包裹着klass对象,即要测试的类对象),
//每次该构造方法执行,klass对象将被扫描类当中的注解annotations
//这个操作过程是十分昂贵的(希望将来JDK将会优化它),因此,请尽量共享TestClass的实例。
public TestClass(Class<?> klass)
{
fClass = klass;
if (klass != null && klass.getConstructors().length > 1)
throw new IllegalArgumentException("Test class can only have one constructor"); //遍历该类和其父类
for (Class<?> eachClass : getSuperClasses(fClass))
{
//扫描该方法的注解
for (Method eachMethod : eachClass.getDeclaredMethods())
addToAnnotationLists(new FrameworkMethod(eachMethod), fMethodsForAnnotations);
for (Field eachField : eachClass.getDeclaredFields())
addToAnnotationLists(new FrameworkField(eachField), fFieldsForAnnotations);
}
} //将该方法或者给属性拥有的注解,全部加入映射
private <T extends FrameworkMember<T>> void addToAnnotationLists(T member, Map<Class<?>, List<T>> map)
{
//遍历该member上的annotation
for (Annotation each : member.getAnnotations())
{
Class<? extends Annotation> type = each.annotationType();
//获取该annotation的Type对应的List
List<T> members = getAnnotatedMembers(map, type);
//防止重复加入List (子类父类的情况,执行子类的)
if (member.isShadowedBy(members))
return;
//如果是Before或者BeforeClass注解,insert到第一个位置
if (runsTopToBottom(type))
members.add(0, member);
//其余(test,after,afterclass)insert到后面
else
members.add(member);
}
} //返回跟annotationClass相同类型的Method集合
public List<FrameworkMethod> getAnnotatedMethods(Class<? extends Annotation> annotationClass)
{
return getAnnotatedMembers(fMethodsForAnnotations, annotationClass);
} //返回跟annotationClass相同类型的Field集合
public List<FrameworkField> getAnnotatedFields(Class<? extends Annotation> annotationClass)
{
return getAnnotatedMembers(fFieldsForAnnotations, annotationClass);
} //获取该annotation的Type对应的List
private <T> List<T> getAnnotatedMembers(Map<Class<?>, List<T>> map, Class<? extends Annotation> type)
{
if (!map.containsKey(type))
map.put(type, new ArrayList<T>());
return map.get(type);
} //确保后加入的before放到前面
private boolean runsTopToBottom(Class<? extends Annotation> annotation)
{
return annotation.equals(Before.class) || annotation.equals(BeforeClass.class);
} //将该类和父类所有Class对象放入List当中返回
private List<Class<?>> getSuperClasses(Class<?> testClass)
{
ArrayList<Class<?>> results = new ArrayList<Class<?>>();
Class<?> current = testClass;
while (current != null)
{
results.add(current);
current = current.getSuperclass();
}
return results;
} /**
* Returns the underlying Java class.
*/
public Class<?> getJavaClass()
{
return fClass;
} /**
* Returns the class's name.
*/
public String getName()
{
if (fClass == null)
return "null";
return fClass.getName();
} /**
* Returns the only public constructor in the class, or throws an {@code
* AssertionError} if there are more or less than one.
*/ public Constructor<?> getOnlyConstructor()
{
Constructor<?>[] constructors = fClass.getConstructors();
Assert.assertEquals(1, constructors.length);
return constructors[0];
} /**
* Returns the annotations on this class
*/
public Annotation[] getAnnotations()
{
if (fClass == null)
return new Annotation[0];
return fClass.getAnnotations();
} public <T> List<T> getAnnotatedFieldValues(Object test, Class<? extends Annotation> annotationClass, Class<T> valueClass)
{
List<T> results = new ArrayList<T>();
for (FrameworkField each : getAnnotatedFields(annotationClass))
{
try
{
results.add(valueClass.cast(each.get(test)));
}
catch(IllegalAccessException e)
{
throw new RuntimeException("How did getFields return a field we couldn't access?");
}
}
return results;
}
}

  

下面是一个测试实例,通过使用TestClass类测试TestUnit的几个方法

package mytest.TestClass;

import java.util.List;

import org.junit.Test;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.TestClass; /**
* org.junit.runners.model.FrameworkMethod 封装一个被测试的方法
*
* @Test 、@Before、@After、@BeforeClass、@AfterClass、@Ignore
*/
public class TestClassDemo
{
public static void test() throws Throwable
{
TestClass klass = new TestClass(TestUnit.class);
System.out.println(klass.getName());
List<FrameworkMethod> list = klass.getAnnotatedMethods(Test.class);
for (FrameworkMethod fm : list)
{
try
{
fm.invokeExplosively((TestUnit) klass.getJavaClass().newInstance(), new Object[0]);
}
catch(Throwable e)
{
System.out.println(e);
} finally
{
System.out.println(fm.getName() + " invoked!");
}
}
} public static void main(String[] args) throws Throwable
{
TestClassDemo.test();
}
}

  

package mytest.TestClass;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class TestUnit
{
public TestUnit()
{
} @Test
public void addx(int x)
{
assertEquals(5, 1 + x);
} @Test
public void add()
{
assertEquals(5.0, 4.0, 0.1);
} @Test
public void hello()
{
assertEquals(5.0, 4.0, 0.1);
}
}

输出结果是:

mytest.TestClass.TestUnit
java.lang.IllegalArgumentException: wrong number of arguments
addx invoked!
java.lang.AssertionError: expected:<5.0> but was:<4.0>
hello invoked!
java.lang.AssertionError: expected:<5.0> but was:<4.0>
add invoked!

  

Junit 源码剖析(一)的更多相关文章

  1. Junit 源码剖析(二)

    junit4 下的所有的testcase都是在Runner下执行的, 可以将Runner理解为junit运行的容器, 默认情况下junit会使用JUnit4ClassRunner作为所有testcas ...

  2. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  3. Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

    声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...

  4. Apache Spark源码剖析

    Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著   ISBN 978-7-121-25420- ...

  5. 基于mybatis-generator-core 1.3.5项目的修订版以及源码剖析

    项目简单说明 mybatis-generator,是根据数据库表.字段反向生成实体类等代码文件.我在国庆时候,没事剖析了mybatis-generator-core源码,写了相当详细的中文注释,可以去 ...

  6. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

  7. SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

    SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...

  8. 自己实现多线程的socket,socketserver源码剖析

    1,IO多路复用 三种多路复用的机制:select.poll.epoll 用的多的两个:select和epoll 简单的说就是:1,select和poll所有平台都支持,epoll只有linux支持2 ...

  9. Java多线程9:ThreadLocal源码剖析

    ThreadLocal源码剖析 ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是 ...

随机推荐

  1. python如何使用 os.path.exists()--Learning from stackoverflow 分类: python 2015-04-23 20:48 139人阅读 评论(0) 收藏

    Q&A参考连接 Problem:IOError: [Errno 2] No such file or directory. os.path.exists() 如果目录不存在,会返回一个0值. ...

  2. hdu1864 最大报销额(01背包)

    转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1864 Problem ...

  3. LabVIEW的错误簇以及错误处理函数

    我们可以在LabVIEW的Modern>>Array, Matrix & Cluster控件面板找到表示错误簇数据类型的错误输入(Error In)以及错误输出(Error Out ...

  4. (转)Java Ant build.xml详解

    1,什么是ant ant是构建工具2,什么是构建概念到处可查到,形象来说,你要把代码从某个地方拿来,编译,再拷贝到某个地方去等等操作,当然不仅与此,但是主要用来干这个3,ant的好处跨平台   --因 ...

  5. 一些as的配置

    打开Android Studio首选项对话框(Mac用户选择Android Studio菜单,Windows用户选择File → Settings菜单).分别展开Editor和Code Style选项 ...

  6. eclipse中svn插件的安装

    Svn(Subversion)是近年来崛起的版本管理工具,在当前的开源项目里(J2EE),几乎95%以上的项目都用到了SVN.Subversion项目的初衷是为了替换当年开源社区最为流行的版本控制软件 ...

  7. JVM体系结构

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/5187049.html ...

  8. css动画结束后 js无法修改translated值 .

    由于项目的需要,俺要做一些页面的转场动画. 即将是移动端,肯定是首先css动画了. 结果确发现,css动画中,如果设置animation-fill-mode: both;在动画结束后无法个性trans ...

  9. NHibernate动态加载资源文件

    最近做项目,又用到了以前做过的ORM框架--NHibernate. 此次想要实现的目标: 1.简单SQL用NHibernate的Session的CRUD方法实现 2.复杂SQL用Native SQL实 ...

  10. 译文:如何使用SocketAsyncEventArgs类(How to use the SocketAsyncEventArgs class)

      转载自: http://blog.csdn.net/hulihui/article/details/3244520 原文:How to use the SocketAsyncEventArgs c ...