JUnit4 源码阅读(二)
来源:互联网 发布:中山瑞达软件 编辑:程序博客网 时间:2024/06/05 11:47
JUnit4 源码阅读(二) - 模型类 解读与分析
如下图是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的子类。
关于各个方法,请看注释
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; }
- JUnit4 源码阅读(二)
- JUnit4 源码阅读(一)
- Xwork2 源码阅读(二)
- live555源码阅读二
- 源码阅读(二)
- OSCache源码阅读(二)
- Log4j源码阅读二
- pg源码阅读二
- STL源码阅读(二)
- Tomcat源码阅读二
- 阅读XRecyclerView源码二
- JUNIT4.11源码阅读(一)--org.junit.Assert类
- FastDFS源码阅读笔记(二)
- Axel源码阅读笔记<二>
- libevent源码阅读笔记(二)
- memcached源码阅读笔记(二)
- WINVNC源码阅读(二)
- Commons DbUtils源码阅读二
- maven安装与环境配置
- JAVA进程CPU占用过高快速定位
- 92A. Chips
- MySQL DBA的基础面试题目
- postman安装
- JUnit4 源码阅读(二)
- eclipse ***.jar cannot be read or is not a valid ZIP fileBuild Path Problem
- Linux的proc文件系统
- MYSQL重点笔记
- ubuntu下复制大文件
- 完美人如何培养自己的气质
- csu1106记忆化搜索
- 【简书如何插入代码框】
- JAVA学习笔记_Junit测试简单使用_assertEquals