Junit 源码剖析(二)
junit4 下的所有的testcase都是在Runner下执行的, 可以将Runner理解为junit运行的容器, 默认情况下junit会使用JUnit4ClassRunner作为所有testcase的执行容器。
如果要定制自己的junit, 则可以实现自己的Runner,最简单的办法就是Junit4ClassRunner继承, spring-test, unitils这些框架就是采用这样的做法。
如在spring中是SpringJUnit4ClassRunner, 在unitils中是UnitilsJUnit4TestClassRunner, 一般我们的testcase都是在通过eclipse插件来执行的, eclipse的junit插件会在执行的时候会初始化指定的Runner。初始化的过程可以在ClassRequest中找到。
org.junit.internal.runners.Junit4ClassRunner
package org.junit.internal.runners; import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List; import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.Filterable;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runner.manipulation.Sortable;
import org.junit.runner.manipulation.Sorter;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner; /**
* @deprecated Included for backwards compatibility with JUnit 4.4. Will be
* removed in the next release. Please use
* {@link BlockJUnit4ClassRunner} in place of
* {@link JUnit4ClassRunner}.
*
* This may disappear as soon as 1 April 2009
*/
@Deprecated
public class JUnit4ClassRunner extends Runner implements Filterable, Sortable
{
private final List<Method> fTestMethods; private TestClass fTestClass; //将要测试的TestCase实例Class对象传入
public JUnit4ClassRunner(Class<?> klass) throws InitializationError
{
fTestClass = new TestClass(klass);
fTestMethods = getTestMethods();
//对要进行测试的方法展开验证
validate();
} //获取@test的方法List
protected List<Method> getTestMethods()
{
return fTestClass.getTestMethods();
} //对要进行测试的方法展开验证
protected void validate() throws InitializationError
{
MethodValidator methodValidator = new MethodValidator(fTestClass);
methodValidator.validateMethodsForDefaultRunner();
methodValidator.assertValid();
} @Override
public void run(final RunNotifier notifier)
{
new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable()
{
public void run()
{
runMethods(notifier);
}
}).runProtected();
} protected void runMethods(final RunNotifier notifier)
{
for (Method method : fTestMethods)
invokeTestMethod(method, notifier);
} @Override
public Description getDescription()
{
Description spec = Description.createSuiteDescription(getName(), classAnnotations());
List<Method> testMethods = fTestMethods;
for (Method method : testMethods)
spec.addChild(methodDescription(method));
return spec;
} protected Annotation[] classAnnotations()
{
return fTestClass.getJavaClass().getAnnotations();
} protected String getName()
{
return getTestClass().getName();
} protected Object createTest() throws Exception
{
return getTestClass().getConstructor().newInstance();
} protected void invokeTestMethod(Method method, RunNotifier notifier)
{
Description description = methodDescription(method);
Object test;
try
{
test = createTest();
}
catch(InvocationTargetException e)
{
testAborted(notifier, description, e.getCause());
return;
}
catch(Exception e)
{
testAborted(notifier, description, e);
return;
}
TestMethod testMethod = wrapMethod(method);
new MethodRoadie(test, testMethod, notifier, description).run();
} private void testAborted(RunNotifier notifier, Description description, Throwable e)
{
notifier.fireTestStarted(description);
notifier.fireTestFailure(new Failure(description, e));
notifier.fireTestFinished(description);
} protected TestMethod wrapMethod(Method method)
{
return new TestMethod(method, fTestClass);
} protected String testName(Method method)
{
return method.getName();
} protected Description methodDescription(Method method)
{
return Description.createTestDescription(getTestClass().getJavaClass(), testName(method), testAnnotations(method));
} protected Annotation[] testAnnotations(Method method)
{
return method.getAnnotations();
} public void filter(Filter filter) throws NoTestsRemainException
{
for (Iterator<Method> iter = fTestMethods.iterator(); iter.hasNext();)
{
Method method = iter.next();
if (!filter.shouldRun(methodDescription(method)))
iter.remove();
}
if (fTestMethods.isEmpty())
throw new NoTestsRemainException();
} public void sort(final Sorter sorter)
{
Collections.sort(fTestMethods, new Comparator<Method>()
{
public int compare(Method o1, Method o2)
{
return sorter.compare(methodDescription(o1), methodDescription(o2));
}
});
} protected TestClass getTestClass()
{
return fTestClass;
}
}
org.junit.internal.runners.TestClass类
不同于上一节提到的org.junit.runners.model.TestClass类
package org.junit.internal.runners; import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runners.BlockJUnit4ClassRunner; /**
* @deprecated Included for backwards compatibility with JUnit 4.4. Will be
* removed in the next release. Please use
* {@link BlockJUnit4ClassRunner} in place of
* {@link JUnit4ClassRunner}.
*/
@Deprecated
public class TestClass
{
private final Class<?> fClass; public TestClass(Class<?> klass)
{
fClass = klass;
} public List<Method> getTestMethods()
{
return getAnnotatedMethods(Test.class);
} List<Method> getBefores()
{
return getAnnotatedMethods(BeforeClass.class);
} List<Method> getAfters()
{
return getAnnotatedMethods(AfterClass.class);
} public List<Method> getAnnotatedMethods(Class<? extends Annotation> annotationClass)
{
List<Method> results = new ArrayList<Method>();
for (Class<?> eachClass : getSuperClasses(fClass))
{
Method[] methods = eachClass.getDeclaredMethods();
for (Method eachMethod : methods)
{
Annotation annotation = eachMethod.getAnnotation(annotationClass);
if (annotation != null && !isShadowed(eachMethod, results))
results.add(eachMethod);
}
}
if (runsTopToBottom(annotationClass))
Collections.reverse(results);
return results;
} private boolean runsTopToBottom(Class<? extends Annotation> annotation)
{
return annotation.equals(Before.class) || annotation.equals(BeforeClass.class);
} private boolean isShadowed(Method method, List<Method> results)
{
for (Method each : results)
{
if (isShadowed(method, each))
return true;
}
return false;
} private boolean isShadowed(Method current, Method previous)
{
if (!previous.getName().equals(current.getName()))
return false;
if (previous.getParameterTypes().length != current.getParameterTypes().length)
return false;
for (int i = 0; i < previous.getParameterTypes().length; i++)
{
if (!previous.getParameterTypes()[i].equals(current.getParameterTypes()[i]))
return false;
}
return true;
} 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;
} public Constructor<?> getConstructor() throws SecurityException, NoSuchMethodException
{
return fClass.getConstructor();
} public Class<?> getJavaClass()
{
return fClass;
} public String getName()
{
return fClass.getName();
} }
org.junit.internal.runners.MethodValidator类
用于在org.junit.internal.runners.Junit4ClassRunner 类当中进行方法验证
package org.junit.internal.runners; import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List; import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runners.BlockJUnit4ClassRunner; /**
* @deprecated Included for backwards compatibility with JUnit 4.4. Will be
* removed in the next release. Please use
* {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
*/
@Deprecated
public class MethodValidator { private final List<Throwable> fErrors= new ArrayList<Throwable>(); private TestClass fTestClass; //org.junit.internal.runners.JUnit4ClassRunner类中
//validate()方法中调用该构造函数-----第一步
public MethodValidator(TestClass testClass) {
fTestClass = testClass;
} public void validateInstanceMethods() {
validateTestMethods(After.class, false);
validateTestMethods(Before.class, false);
validateTestMethods(Test.class, false); List<Method> methods= fTestClass.getAnnotatedMethods(Test.class);
if (methods.size() == 0)
fErrors.add(new Exception("No runnable methods"));
} public void validateStaticMethods() {
validateTestMethods(BeforeClass.class, true);
validateTestMethods(AfterClass.class, true);
} //Junit4ClassRunner类中调用第二步
public List<Throwable> validateMethodsForDefaultRunner() {
//校验无参构造方法
validateNoArgConstructor();
//校验注解方法
validateStaticMethods();
validateInstanceMethods();
return fErrors;
} //Junit4ClassRunner类中第三步
public void assertValid() throws InitializationError {
if (!fErrors.isEmpty())
throw new InitializationError(fErrors);
} public void validateNoArgConstructor() {
try {
fTestClass.getConstructor();
} catch (Exception e) {
fErrors.add(new Exception("Test class should have public zero-argument constructor", e));
}
} //校验要测试的方法 是否静态方法、是否public方法、是否返回值void、是否无参数
//@param annotation 要测试的annotation类
//@param isStatic 是否要求静态方法
private void validateTestMethods(Class<? extends Annotation> annotation,boolean isStatic) {
List<Method> methods= fTestClass.getAnnotatedMethods(annotation); for (Method each : methods) {
if (Modifier.isStatic(each.getModifiers()) != isStatic) {
String state= isStatic ? "should" : "should not";
fErrors.add(new Exception("Method " + each.getName() + "() "
+ state + " be static"));
}
if (!Modifier.isPublic(each.getDeclaringClass().getModifiers()))
fErrors.add(new Exception("Class " + each.getDeclaringClass().getName()
+ " should be public"));
if (!Modifier.isPublic(each.getModifiers()))
fErrors.add(new Exception("Method " + each.getName()
+ " should be public"));
if (each.getReturnType() != Void.TYPE)
fErrors.add(new Exception("Method " + each.getName()
+ " should be void"));
if (each.getParameterTypes().length != 0)
fErrors.add(new Exception("Method " + each.getName()
+ " should have no parameters"));
}
}
}
Junit 源码剖析(二)的更多相关文章
- Django Rest Framework源码剖析(二)-----权限
一.简介 在上一篇博客中已经介绍了django rest framework 对于认证的源码流程,以及实现过程,当用户经过认证之后下一步就是涉及到权限的问题.比如订单的业务只能VIP才能查看,所以这时 ...
- Qt信号槽源码剖析(二)
大家好,我是IT文艺男,来自一线大厂的一线程序员 上节视频给大家讲解了Qt信号槽的基本概念.元对象编译器.示例代码以及Qt宏:今天接着深入分析,进入Qt信号槽源码剖析系列的第二节视频. Qt信号槽的宏 ...
- jdk源码剖析二: 对象内存布局、synchronized终极原理
很多人一提到锁,自然第一个想到了synchronized,但一直不懂源码实现,现特地追踪到C++层来剥开synchronized的面纱. 网上的很多描述大都不全,让人看了不够爽,看完本章,你将彻底了解 ...
- Junit 源码剖析(一)
采用Junit4.8.2分析Junit实现架构 源码架构两个大包:junit包 org包 首先分析org.junit.runners.model包下的几个类 org.junit.runners.mod ...
- Dubbo源码剖析二之注册中心
Dubbo基础二之架构及处理流程概述 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中架构中,无论是服务提供者还是服务消费者都离不开注册中心,可见注册中心之重要.Redis.Nacos. ...
- muduo库源码剖析(二) 服务端
一. TcpServer类: 管理所有的TCP客户连接,TcpServer供用户直接使用,生命期由用户直接控制.用户只需设置好相应的回调函数(如消息处理messageCallback)然后TcpSer ...
- boost.asio源码剖析(二) ---- 架构浅析
* 架构浅析 先来看一下asio的0层的组件图. (图1.0) io_object是I/O对象的集合,其中包含大家所熟悉的socket.deadline_tim ...
- jdk源码剖析:Synchronized
开启正文之前,先说一下源码剖析这一系列,就以"死磕到底"的精神贯彻始终,最少追踪到JVM指令(再往下C语言实现了). =========正文分割线=========== Sync ...
- jdk源码剖析一:OpenJDK-Hotspot源码包目录结构
开启正文之前,先说一下源码剖析这一系列,就以“死磕到底”的精神贯彻始终,JDK-->JRE-->JVM(以openJDK代替) 最近想看看JDK8源码,但JDK中JVM(安装在本地C:\P ...
随机推荐
- 【设计模式 - 22】之策略模式(Strategy)
1 模式简介 在策略模式中,一个类的行为或其算法可以在运行时改变.策略模式定义了一系列算法,把它们一个个封装起来,并且使它们可以互相替换. 策略模式的优点: 1) 算法可以自由 ...
- 关于wordpress在修改固定链接后,总显示Not Found的问题
参考来源: http://chinablog.blog.51cto.com/276793/280278 一.问题背景 使用wordpress搭建网站,为了让文章URL看起来漂亮一点,wordpress ...
- Hive sql 语法解读
一. 创建表 在官方的wiki里,example是这种: Sql代码 CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name [(col_name d ...
- careercup-数组和字符串1.4
1.4 编写一个方法,将字符串中的空格全部替换为“%20“.假定该字符串尾部有足够的空间存放新增字符,并且知道字符串的”真实“长度. C++实现代码: #include<iostream> ...
- 如何使用JCA (J2EE 连接器架构)实现企业应用--转载
JCA (J2EE 连接器架构,Java Connector Architecture)是对J2EE标准集的重要补充.因为它注重的是将Java程序连接到非Java程序和软件包中间件的开发.连接器特指基 ...
- System Operations on AWS - Lab 3W - Managing Storage (Windows)
创建一个名叫Processor的EC2实例,登陆到CommandHost实例,通过AWS CLI对Processor实例的EBS卷做snapshot,设置周期性snapshot的计划任务, 登陆到Pr ...
- 批处理,修改环境变量path的方法(加环境变量)
方法一:批处理中,修改环境变量,一次性有效(也就是在当前的脚本中有效) CMD中运行 set path==%path%;d:/mypath 用 set path可以查看,当前的环境变量 方法二 :批处 ...
- C#语法糖之第四篇: 扩展方法
今天继续分享C#4.0语法糖的扩展方法,这个方法也是我本人比较喜欢的方法.大家先想想比如我们以前写的原始类型不能满足现在的需求,而需要在该类型中添加新的方法来实现时大家会怎么做.我先说一下我没有学习到 ...
- Spring各种传播特性源码实现的概览
这几天都在分析Spring的源码实现,看到事务管理的部分 我们知道事务的传播特性有下面几种,我标黄的就是最常用的3中传播特性, Sping在发生事务嵌套的时候,会依据内层事务的传播特性,来决定内层是事 ...
- Nico Game Studio 1.基本UI和地图编辑基础功能
完成了基本界面. 本来想自画UI,但是考虑到工作量较大和美观程度有限,以及工具使用对象是比较初级玩家,处于性价比和最初目的,放弃了自绘.