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

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() : int
equals(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;
    }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值