Junit 源码剖析(一)

采用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!

  

时间: 2024-10-09 23:24:43

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

Spring框架AOP源码剖析

今天我要和大家分享的是 AOP(Aspect-Oriented Programming)这个东西的源码剖析,作为多年的开发者,想必大家在面试的时候都被问过,你知道Spring框架AOP的底层实现机制吗,这可是很简单的噢,我们会说,如果某个类有接口就使用JDK动态代理,没有接口就用CGLIB动态代理,并且Spring也提供了可配置开关,不管有无接口都一律使用CGLIB动态代理,例如 <aop:aspectj-autoproxy proxy-target-class="true"/&

下载-深入浅出Netty源码剖析、Netty实战高性能分布式RPC、NIO+Netty5各种RPC架构实战演练三部曲视频教程

下载-深入浅出Netty源码剖析.Netty实战高性能分布式RPC.NIO+Netty5各种RPC架构实战演练三部曲视频教程 第一部分:入浅出Netty源码剖析 第二部分:Netty实战高性能分布式RPC 第三部分:NIO+Netty5各种RPC架构实战演练

Phaser实现源码剖析

在这里首先说明一下,由于Phaser在4.3代码里是存在,但并没有被开放出来供使用,但已经被本人大致研究了,因此也一并进行剖析. Phaser是一个可以重复利用的同步栅栏,功能上与CyclicBarrier和CountDownLatch相似,不过提供更加灵活的用法.也就是说,Phaser的同步模型与它们差不多.一般运用的场景是一组线程希望同时到达某个执行点后(先到达的会被阻塞),执行一个指定任务,然后这些线程才被唤醒继续执行其它任务. Phaser一般是定义一个parties数(parties一

【Java集合源码剖析】HashMap源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/36034955 HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap. HashMap 实现了Serializable接口,因此它支持序列化,

转:【Java集合源码剖析】Vector源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35793865   Vector简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是相对安全,有些时候还是要加入同步语句来保证线程的安全),可以用于多线程环境. Vector没有丝线Serializable接口,因此它不支持序列化,实现了Cloneable接口,能被克隆,实

下载BootStrap企业级应用培训课程(零基础、源码剖析,内部教材,项目实训)

全套500多课,附赠JS OOP编程,转一播放码.下载地址:http://pan.baidu.com/s/1kVLdZmf 第一季:基础篇,侧重于BootStrap 相关 API 详解.主要包含以下内容:Brackets前端开发工具详解.BootStrap框架三大核心-CSS.BootStrap框架三大核心-布局组件.BootStrap框架三大核心-JavaScript插件.附-BootStrap编码规范第二季:高级篇,侧重于BootStap源码解析与第三方扩展.主要包含以下内容:BootStr

菜鸟nginx源码剖析 框架篇(一) 从main函数看nginx启动流程(转)

俗话说的好,牵牛要牵牛鼻子 驾车顶牛,处理复杂的东西,只要抓住重点,才能理清脉络,不至于深陷其中,不能自拔.对复杂的nginx而言,main函数就是“牛之鼻”,只要能理清main函数,就一定能理解其中的奥秘,下面我们就一起来研究一下nginx的main函数. 1.nginx的main函数解读 nginx启动显然是由main函数驱动的,main函数在在core/nginx.c文件中,其源代码解析如下,涉及到的数据结构在本节仅指出其作用,将在第二节中详细解释. nginx main函数的流程图如下:

HashMap(2) 源码剖析(推荐)

今天看代码,想到去年发生的HashMap发生的CPU使用率100%的事件,转载下当时看的三个比较不错的博客(非常推荐) 参考:http://coolshell.cn/articles/9606.html   http://github.thinkingbar.com/hashmap-analysis/ http://developer.51cto.com/art/201102/246431.htm 在 Java 集合类中,使用最多的容器类恐怕就是 HashMap 和 ArrayList 了,所以

菜鸟nginx源码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)

Author:Echo Chen(陈斌) Email:[email protected] Blog:Blog.csdn.net/chen19870707 Date:October 31h, 2014 1.哈希表ngx_hash_t的优势和特点 哈希表是一种典型的以空间换取时间的数据结构,在没有冲突的情况下,对任意元素的插入.索引.删除的时间复杂度都是O(1).这样优秀的时间复杂度是通过将元素的key值以hash方法f映射到哈希表中的某一个位置来访问记录来实现的,即键值为key的元素必定存储在哈希