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 BlockJUnit4ClassRunner
public 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
构建的时序图