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!