1.Runner
上一节讲到了Junit的运行实际上是调用Runner中的run方法执行的,那么接下来总结一下Runner,首先我们看下Runner的类图

2.Runner的构建
让我们回到类Request.class中的classes方法
public static Request classes(Computer computer, Class<?>... classes) {
try {
AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
Runner suite = computer.getSuite(builder, classes);
return runner(suite);
} catch (InitializationError e) {
throw new RuntimeException(
"Bug in saff's brain: Suite constructor, called as above, should always complete");
}
}
由此可知第一个Runner是由computer.getSuite(builder, classes)中获得,参数builder是直接new AllDefaultPossibilitiesBuilder,从名称可以看出,这个builder是所有默认的可能builder,看起来很牛逼的样子,点进去可以发现构造方法只有一个属性赋值,先忽略,继续看computer.getSuite(builder, classes)
public Runner getSuite(final RunnerBuilder builder,
Class<?>[] classes) throws InitializationError {
return new Suite(new RunnerBuilder() {
@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable {
return getRunner(builder, testClass);
}
}, classes);
}
protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
return builder.runnerForClass(testClass);
}
由此可见这个Runner是直接new的一个Suite,进入 Suite类看该处调用的构造方法,
public Suite(RunnerBuilder builder, Class<?>[] classes) throws InitializationError {
this(null, builder.runners(null, classes));
}
protected Suite(Class<?> klass, List<Runner> runners) throws InitializationError {
super(klass);
this.runners = Collections.unmodifiableList(runners);
}
可见最终是调用
Suite的protected Suite(Class<?> klass, List<Runner> runners)构造方法构建的Suite,该参数中的
List<Runner> runners是由builder.runners而来,而由前而的调用链可知,这个builder是在Computer类中getSuite的方法中,在new Suite时直接new的一个匿名的RunnerBuilder该
RunnerBuilder实现了runnerForClass方法,而该方法的具体实现是由上文(Request类classes方法)中AllDefaultPossibilitiesBuilder来实现的,该处又是一个钩子方法,即当调用这个Suite对象的runnerForClass方法时,实际调用的是AllDefaultPossibilitiesBuilder的runnerForClass。-
让我们回到
Suite的构建中,由刚才的构造方法可以看出,在构建Suite之前先调用了匿名RunnerBuilder实例中的runners,继续看这个RunnerBuilder.runners都做了什么public List<Runner> runners(Class<?> parent, Class<?>[] children) throws InitializationError { //添加父class,此时由Suite调用过来时为null addParent(parent); try { //此时的children是从开始一路传递下来的测试类的class return runners(children); } finally { //移除父class removeParent(parent); } }
继续看该方法中调用的runners方法
private List<Runner> runners(Class<?>[] children) {
ArrayList<Runner> runners = new ArrayList<Runner>();
for (Class<?> each : children) {
//循环遍历所有测试类的class,构建Runner
//由此可以发现每个测试类对应一个Runner
Runner childRunner = safeRunnerForClass(each);
if (childRunner != null) {
runners.add(childRunner);
}
}
return runners;
}
继续看safeRunnerForClass方法
public Runner safeRunnerForClass(Class<?> testClass) {
try {
return runnerForClass(testClass);
} catch (Throwable e) {
return new ErrorReportingRunner(testClass, e);
}
}
- 该方法实际是调用的
runnerForClass来得到的Runner,再看下方法所在类:抽象类RunnerBuilder方法中 - 还记得我们现在是哪个环节中嘛,正是在
new Suite的构建方法中,所以这个地方的玄机就在于,该处的RunnerBuilder是前创建new Suite时创建的匿名RunnerBuilder,而这个匿名RunnerBuilder的runnerForClass正是由Request.classes方法中new的AllDefaultPossibilitiesBuilder对象来实现的。
这就是钩子方法的妙用,也称为模板方法
接下来就让我们看下AllDefaultPossibilitiesBuilder的runnerForClass
让我们来看下调用时序

3.RunnerBuilder
首先来看上文中调用的AllDefaultPossibilitiesBuilder.runnerForClass
public Runner runnerForClass(Class<?> testClass) throws Throwable {
//初始化RunnerBuilder列表
//该处将Junit所有的RunnerBuilder都创建好放入集合
//从该处可以以知道该类为什么叫AllDefaultPossibilitiesBuilder了
List<RunnerBuilder> builders= Arrays.asList(
ignoredBuilder(), //IgnoredBuilder需要忽略测试的RunnerBuilder
annotatedBuilder(), //AnnotatedBuilder带有注解的RunnerBuilder
suiteMethodBuilder(), //SuiteMethodBuilder
junit3Builder(), //JUnit3Builder(目测是为了兼容junit3)
junit4Builder()); //JUnit4Builder
for (RunnerBuilder each : builders) {
Runner runner= each.safeRunnerForClass(testClass);
if (runner != null)
return runner;
}
return null;
}
- 由以上代码可以发现,在
AllDefaultPossibilitiesBuilder中,首先把所有RunnerBuilder都构建好 - 然后循环遍历,调用每一个
builder的safeRunnerForClass方法 - 进入该方法不难发现实际是调用每一个builder的
runnerForClass方法,只要命中任何一个builder的构建规则,即使用该builder创建Rnner,然后退出循环 - 该处应用正是一个责任链模式
让我们逐个看看各个builder的runnerForClass
-
IgnoredBuilder:public Runner runnerForClass(Class<?> testClass) { if (testClass.getAnnotation(Ignore.class) != null) return new IgnoredClassRunner(testClass); } return null; }
可见如果测试类中加上了@Ignore则会使用该builder
-
AnnotatedBuilder:public Runner runnerForClass(Class<?> testClass) throws Exception { //获取测试类中用RunWith注解标注的类 RunWith annotation= testClass.getAnnotation(RunWith.class); if (annotation != null) return buildRunner(annotation.value(), testClass); return null; } public Runner buildRunner(Class<? extends Runner> runnerClass, Class<?> testClass) throws Exception { try { ////创建RunWith注解中声明类的实例,并将测试类的class传入 return runnerClass.getConstructor(Class.class).newInstance( new Object[] { testClass }); } catch (NoSuchMethodException e) { //先忽略异常处理 }
- 由该段代码可知,如果测试中在
@RunWith注解中指定了Runner,则使用该builder,并使用反射创建指定的Runner - 看到此,相信你知道为什么我们在用Junit写单测的时候,单测类上面注解@RunWith的用途了
-
SuiteMethodBuilder:
进入suiteMethodBuilder()方法protected RunnerBuilder suiteMethodBuilder() { if (fCanUseSuiteMethod) return new SuiteMethodBuilder(); return new NullBuilder(); }
首先我们看到的是不是直接new SuiteMethodBuilder,而是首先看fCanUseSuiteMethod属性是否为true,还记得该属性是什么时候赋值的嘛?返回去看下Request.classes方法
public static Request classes(Computer computer, Class<?>... classes) {
try {
AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
Runner suite = computer.getSuite(builder, classes);
return runner(suite);
} catch (InitializationError e) {
throw new RuntimeException(
"Bug in saff's brain: Suite constructor, called as above, should always complete");
}
}
正是new AllDefaultPossibilitiesBuilder(true); 中的true
也就是说,测试类中如果没有ignored和annotated,首先使用的RunnerBuilder实际是SuiteMethodBuilder,并执行该builder的runnerForClass方法,进入一探究竟
public Runner runnerForClass(Class<?> each) throws Throwable {
//判断测试的class中是否有suite方法
if (hasSuiteMethod(each))
//创建suiteMethod,该类继承自JUnit38ClassRunner
return new SuiteMethod(each);
return null;
}
public boolean hasSuiteMethod(Class<?> testClass) {
try {
testClass.getMethod("suite");
} catch (NoSuchMethodException e) {
return false;
}
return true;
}
- 根据此段代码可以发现,如果测试类中没有
suite方法的话返回的是null - 此时在
AllDefaultPossibilitiesBuilder.runnerForClass中会继续循环寻找下一下builder
JUnit3Builder:如果测试类使用的是Junit3的测试方式,则使用该builder,�该builder不再细看-
JUnit4Builder:该builder很简单,直接new BlockJUnit4ClassRunnerpublic Runner runnerForClass(Class<?> testClass) throws Throwable { return new BlockJUnit4ClassRunner(testClass); }
- 从以上
AllDefaultPossibilitiesBuilder.runnerForClass方法的执行看来,Junit4默认使用的builder是BlockJUnit4ClassRunner
让我们再回到我们的主线new Suite中,
public Suite(RunnerBuilder builder, Class<?>[] classes) throws InitializationError {
this(null, builder.runners(null, classes));
}
protected Suite(Class<?> klass, List<Runner> runners) throws InitializationError {
super(klass);
//Collections.unmodifiableList将传入的List变得不可修改
this.runners = Collections.unmodifiableList(runners);
}
- 由以上流程可知,在创建
Suite这个Runner时,首先把所有测试类对应的具体Runner通过对应的RunnerBuilder构建好,放入Suite的List<Runner> runners属性中,至此Suite构建完成(构造方法中的super(klass)后续再看)
总之,JunitCore中使用的Runner是直接new的Suite,而这个Suite中属性List<Runner> runners,默认情况下,这些runner都是BlockJUnit4ClassRunner
至此Runner的构建完成,让我来接下来看下整个Runner构建的时序图
4.Runner构建时序图
