zoukankan      html  css  js  c++  java
  • 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!
    

      

  • 相关阅读:
    7.4 List集合
    vue学习笔记
    javaWEB中web.xml配置文件相关
    maven常用dos命令
    Oracle,sqlserver,mySQl的区别和联系:
    oracle数据库视图,序列,索引的sql语句查看
    java 异常处理
    线程专题
    package、folder和source folder的区别
    Java内存分配之堆、栈和常量池
  • 原文地址:https://www.cnblogs.com/wuxinliulei/p/4933668.html
Copyright © 2011-2022 走看看