採用Junit4.8.2分析Junit實現架構java
源碼架構兩個大包:junit包 org包架構
首先分析org.junit.runners.model包下的幾個類less
org.junit.runners.modela.TestClass單元測試
org.junit.runners.modela.FrameworkMethod測試
org.junit.runners.modela.FrameworkMember優化
org.junit.runners.modela.FrameworkFieldthis
涉及到的類:code
org.junit.Assert;對象
org.junit.Before;blog
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!