JUnit4 源码阅读(二)

来源:互联网 发布:中山瑞达软件 编辑:程序博客网 时间:2024/06/05 11:47

JUnit4 源码阅读(二) - 模型类 解读与分析

如下图是runners部分的结构
runners
可以分为两个部分
1. 模型类: 作用域模型,方法模型,测试类模型
2. 执行类Runner: 如ParentRunner, BlockJUnit4ClassRunner, Suite,理解这三个Runner很重要。

作用域模型是对测试类作用域的封装,方法模型是对测试类方法的封装,测试类模型是对测试类的封装。我们知道测试类的作用域,方法,以及类上都有可能存在注解,所以这三个模型都实现了Annotatable注解,2个方法一个是获取所有注解,一个是获取特定注解。

import java.lang.annotation.Annotation;public interface Annotatable {    Annotation[] getAnnotations();    <T extends Annotation> T getAnnotation(Class<T> annotationType);}

作用域模型和方法模型都继承于抽象类FrameMember,这里用到了泛型,同时要求子类泛型类型必须是FrameMemeber的子类。

model

关于各个方法,请看注释

public abstract class FrameworkMember<T extends FrameworkMember<T>> implements        Annotatable {    //是否和otherMember相等    abstract boolean isShadowedBy(T otherMember);    //是否已经存在于members集合中    boolean isShadowedBy(List<T> members) {        for (T each : members) {            if (isShadowedBy(each)) {                return true;            }        }        return false;    }    protected abstract int getModifiers();    //修饰符是否是静态    public boolean isStatic() {        return Modifier.isStatic(getModifiers());    }    //修饰符是否是公共的    public boolean isPublic() {        return Modifier.isPublic(getModifiers());    }    //明称,方法名、作用域名    public abstract String getName();    //作用域类型,方法返回值类型    public abstract Class<?> getType();    //获取声明类    public abstract Class<?> getDeclaringClass();}

作用域模型

FrameworkField封装了一个Field, 对外提供了一些,我们访问field的方法。其中field.get(target),表示获取目标对象target在该作用的值。

public class FrameworkField extends FrameworkMember<FrameworkField> {    private final Field field;    FrameworkField(Field field) {        if (field == null) {            throw new NullPointerException(                    "FrameworkField cannot be created without an underlying field.");        }        this.field = field;    }    @Override    public String getName() {        return getField().getName();    }    public Annotation[] getAnnotations() {        return field.getAnnotations();    }    public <T extends Annotation> T getAnnotation(Class<T> annotationType) {        return field.getAnnotation(annotationType);    }    @Override    public boolean isShadowedBy(FrameworkField otherMember) {        return otherMember.getName().equals(getName());    }    @Override    protected int getModifiers() {        return field.getModifiers();    }    public Field getField() {        return field;    }    @Override    public Class<?> getType() {        return field.getType();    }    @Override    public Class<?> getDeclaringClass() {        return field.getDeclaringClass();    }    public Object get(Object target) throws IllegalArgumentException, IllegalAccessException {        return field.get(target);    }    @Override    public String toString() {        return field.toString();    }}

方法模型

同样FrameworkMethod封装了一个Method对象。
其中invokeExplosively方法接收目标对象和方法所需参数,通过反射来执行目标对象的方法。关于反射,可以参考这篇博文
Java 反射和注解的一些总结

public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {    private final Method method;    /**     * Returns a new {@code FrameworkMethod} for {@code method}     */    public FrameworkMethod(Method method) {        if (method == null) {            throw new NullPointerException(                    "FrameworkMethod cannot be created without an underlying method.");        }        this.method = method;    }    /**     * Returns the underlying Java method     */    public Method getMethod() {        return method;    }    /**     * Returns the result of invoking this method on {@code target} with     * parameters {@code params}. {@link InvocationTargetException}s thrown are     * unwrapped, and their causes rethrown.     */    public Object invokeExplosively(final Object target, final Object... params)            throws Throwable {        return new ReflectiveCallable() {            @Override            protected Object runReflectiveCall() throws Throwable {                return method.invoke(target, params);            }        }.run();    }    /**     * Returns the method's name     */    @Override    public String getName() {        return method.getName();    }    /**     * Adds to {@code errors} if this method:     * <ul>     * <li>is not public, or     * <li>takes parameters, or     * <li>returns something other than void, or     * <li>is static (given {@code isStatic is false}), or     * <li>is not static (given {@code isStatic is true}).     * </ul>     */    public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {        validatePublicVoid(isStatic, errors);        if (method.getParameterTypes().length != 0) {            errors.add(new Exception("Method " + method.getName() + " should have no parameters"));        }    }    /**     * Adds to {@code errors} if this method:     * <ul>     * <li>is not public, or     * <li>returns something other than void, or     * <li>is static (given {@code isStatic is false}), or     * <li>is not static (given {@code isStatic is true}).     * </ul>     */    public void validatePublicVoid(boolean isStatic, List<Throwable> errors) {        if (isStatic() != isStatic) {            String state = isStatic ? "should" : "should not";            errors.add(new Exception("Method " + method.getName() + "() " + state + " be static"));        }        if (!isPublic()) {            errors.add(new Exception("Method " + method.getName() + "() should be public"));        }        if (method.getReturnType() != Void.TYPE) {            errors.add(new Exception("Method " + method.getName() + "() should be void"));        }    }    @Override    protected int getModifiers() {        return method.getModifiers();    }    /**     * Returns the return type of the method     */    public Class<?> getReturnType() {        return method.getReturnType();    }    /**     * Returns the return type of the method     */    @Override    public Class<?> getType() {        return getReturnType();    }    /**     * Returns the class where the method is actually declared     */    @Override    public Class<?> getDeclaringClass() {        return method.getDeclaringClass();    }    public void validateNoTypeParametersOnArgs(List<Throwable> errors) {        new NoGenericTypeParametersValidator(method).validate(errors);    }    //比较方法名,然后参数个数,最后参数类型    @Override    public boolean isShadowedBy(FrameworkMethod other) {        if (!other.getName().equals(getName())) {            return false;        }        if (other.getParameterTypes().length != getParameterTypes().length) {            return false;        }        for (int i = 0; i < other.getParameterTypes().length; i++) {            if (!other.getParameterTypes()[i].equals(getParameterTypes()[i])) {                return false;            }        }        return true;    }    @Override    public boolean equals(Object obj) {        if (!FrameworkMethod.class.isInstance(obj)) {            return false;        }        return ((FrameworkMethod) obj).method.equals(method);    }    @Override    public int hashCode() {        return method.hashCode();    }    /**     * Returns true if this is a no-arg method that returns a value assignable     * to {@code type}     *     * @deprecated This is used only by the Theories runner, and does not     *             use all the generic type info that it ought to. It will be replaced     *             with a forthcoming ParameterSignature#canAcceptResultOf(FrameworkMethod)     *             once Theories moves to junit-contrib.     */    @Deprecated    public boolean producesType(Type type) {        return getParameterTypes().length == 0 && type instanceof Class<?>                && ((Class<?>) type).isAssignableFrom(method.getReturnType());    }    private Class<?>[] getParameterTypes() {        return method.getParameterTypes();    }    /**     * Returns the annotations on this method     */    public Annotation[] getAnnotations() {        return method.getAnnotations();    }    /**     * Returns the annotation of type {@code annotationType} on this method, if     * one exists.     */    public <T extends Annotation> T getAnnotation(Class<T> annotationType) {        return method.getAnnotation(annotationType);    }    @Override    public String toString() {        return method.toString();    }}

测试类模型 TestClass

这个类里面声明了许多方法,而且大部分都用到了泛型,有点不容易读。

作用域

    private final Class<?> clazz; //clazz是测试类对象,(可以为空,这点需要注意,后面有一些为空的情况,到时再说)    //以下List中可能包含超类的作用域或方法,从后面的构造方法可以看出,之后会涉及到    //key为注解,值为带有该注解的所有方法集合    private final Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations;    //key为注解,值为带有该注解的所有作用域集合    private final Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations;

公有方法

类中非公有类型的方法一般都是提供给公有方法使用,私有方法的产生应该就是为了达到公有方法的效果,将公有方法中重复的片段抽取出来,封装成私有方法,供公有方法使用,这样有助于减少代码数量(尽管这样,该类代码还是很长…)
那么我们就先从公有方法说起,从而慢慢渗透。
公有方法

TestClass(Class<?>) : //构造方法getAnnotations() : Annotation[] //获取测试类上的所有注解getAnnotation(Class<T>) : T     //获取测试类上特定注解getAnnotatedMethods() : List<FrameworkMethod> //测试类上有注解的方法集合getAnnotatedMethods(Class<? extends Annotation>): List<FrameworkMethod> //有特定类型注解的方法集合getAnnotatedFields() : List<FrameworkField> //测试类上有注解的作用域集合getAnnotatedFields(Class<? extends Annotation>) : List<FrameworkField> //有特定类型注解的作用域集合//以下方法主要用在 规则测试中//获取所有 带有某种注解的作用域的值,并且作用域类型是T类型或其子类/**eg. * @Rule * public TemporaryFolder folder = new TemporaryFolder(new File("/home/live/IdeaProjects/test")); */getAnnotatedFieldValues(Object, Class<? extends Annotation>, Class<T>): List<T> ////获取目标对象上 所有带有某种注解的方法的返回值,并且方法的返回值类型是T类型或者其子类/**eg. * @Rule * public ExternalResource getTemporarayForlder() { *     System.out.println("方法中"); *     return new TemporaryFolder(new File("/home/live/IdeaProjects/test")); * } */getAnnotatedMethodValues(Object, Class<? extends Annotation>, Class<T>) : List<T> //getJavaClass() : Class<?>   //测试类对象getName() : String          //测试类类名getOnlyConstructor(): Constructor<?>  //唯一的构造函数isPublic() : boolean    isANonStaticInnerClass() : boolean //是非静态内部类吗?hashCode() : intequals(Object): boolean

构造函数
构造方法传入的测试类对象不为空的话,那么构造函数的个数不能大于1。
注解-方法,注解-作用域 这两个map的构造过程是这样的,首先,我们需要从测试类clazz, 获取所有Member(getDeclaredField, getDeclaredMethod), 每一个成员都会有对应的注解(getAnnotations()), 那么就是Member-Annotation[] 的形式,我们需要反过来,以Annotation - List的形式 存储到Map中,以下就是这个过程的代码实现(这个过程还是很绕的…)。

    public TestClass(Class<?> clazz) {        this.clazz = clazz;        if (clazz != null && clazz.getConstructors().length > 1) {            throw new IllegalArgumentException(                    "Test class can only have one constructor");        }        Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations =                new LinkedHashMap<Class<? extends Annotation>, List<FrameworkMethod>>();        Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations =                new LinkedHashMap<Class<? extends Annotation>, List<FrameworkField>>();        scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations);        this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations);        this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations);    }    //浏览测试类及父类的注解成员。    protected void scanAnnotatedMembers(            Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations,            Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations) {        for (Class<?> eachClass : getSuperClasses(clazz)) { //当前类,及所有超类        //(对象声明方法和方法是不一样的,这点注意)            for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) { //对象所有声明方法                addToAnnotationLists(new FrameworkMethod(eachMethod), methodsForAnnotations);            }            for (Field eachField : getSortedDeclaredFields(eachClass)) {//对象所有声明作用域                addToAnnotationLists(new FrameworkField(eachField), fieldsForAnnotations);             }        }    }    //递归获取超类    private static 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;    }    // Member-Annotation[],根据Annotation找出对应List<Member>,添加到其中    protected static <T extends FrameworkMember<T>> void addToAnnotationLists(T member,            Map<Class<? extends Annotation>, List<T>> map) {        for (Annotation each : member.getAnnotations()) {            Class<? extends Annotation> type = each.annotationType();            List<T> members = getAnnotatedMembers(map, type, true);            if (member.isShadowedBy(members)) {                return;            }            if (runsTopToBottom(type)) {                members.add(0, member);            } else {                members.add(member);            }        }    }

获取成员的值,利用反射从目标对象中获取

    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 {                //获取目标对象在作用域each上的值                Object fieldValue = each.get(test);                //判断是否为我们需要的类型                if (valueClass.isInstance(fieldValue)) {                    results.add(valueClass.cast(fieldValue));                }            } catch (IllegalAccessException e) {                throw new RuntimeException(                        "How did getFields return a field we couldn't access?", e);            }        }        return results;    }    public <T> List<T> getAnnotatedMethodValues(Object test,            Class<? extends Annotation> annotationClass, Class<T> valueClass) {        List<T> results = new ArrayList<T>();        for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) {            try {                //判断方法的方法的返回类型是否为我们需要的类型 T                if (valueClass.isAssignableFrom(each.getReturnType())) {                    //是的话,反射执行方法,获取返回值                    Object fieldValue = each.invokeExplosively(test);                    results.add(valueClass.cast(fieldValue));                }            } catch (Throwable e) {                throw new RuntimeException(                        "Exception in " + each.getName(), e);            }        }        return results;    }

以下是获取带有注解的field或method集合的方法实现

    public List<FrameworkMethod> getAnnotatedMethods() {        List<FrameworkMethod> methods = collectValues(methodsForAnnotations);        Collections.sort(methods, METHOD_COMPARATOR);        return methods;    }    public List<FrameworkField> getAnnotatedFields() {        return collectValues(fieldsForAnnotations);    }    //将Map<?, List<T>>的所有值,添加到一个列表中    private <T> List<T> collectValues(Map<?, List<T>> map) {        Set<T> values = new LinkedHashSet<T>();        for (List<T> additionalValues : map.values()) {            values.addAll(additionalValues);        }        return new ArrayList<T>(values);    }    public List<FrameworkMethod> getAnnotatedMethods(            Class<? extends Annotation> annotationClass) {        return Collections.unmodifiableList(getAnnotatedMembers(methodsForAnnotations, annotationClass, false));    }    public List<FrameworkField> getAnnotatedFields(            Class<? extends Annotation> annotationClass) {        return Collections.unmodifiableList(getAnnotatedMembers(fieldsForAnnotations, annotationClass, false));    }    //从Map<Class<? extends Annotation>, List<T>>中获取某Key,对应的值,如果该key不存在,且fillAbsent为真,那么就会在map中放入,大小为0的List对象    private static <T> List<T> getAnnotatedMembers(Map<Class<? extends Annotation>, List<T>> map,            Class<? extends Annotation> type, boolean fillIfAbsent) {        if (!map.containsKey(type) && fillIfAbsent) {            map.put(type, new ArrayList<T>());        }        List<T> members = map.get(type);        return members == null ? Collections.<T>emptyList() : members;    }