1.Runner
上一節(jié)講到了Junit的運(yùn)行實(shí)際上是調(diào)用Runner中的run方法執(zhí)行的官撼,那么接下來總結(jié)一下Runner,首先我們看下Runner的類圖
2.Runner的構(gòu)建
讓我們回到類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");
}
}
由此可知第一個(gè)Runner是由computer.getSuite(builder, classes)
中獲得医男,參數(shù)builder是直接new AllDefaultPossibilitiesBuilder
,從名稱可以看出,這個(gè)builder是所有默認(rèn)的可能builder佑稠,看起來很牛逼的樣子,點(diǎn)進(jìn)去可以發(fā)現(xiàn)構(gòu)造方法只有一個(gè)屬性賦值旗芬,先忽略讶坯,繼續(xù)看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);
}
由此可見這個(gè)Runner
是直接new
的一個(gè)Suite
,進(jìn)入 Suite
類看該處調(diào)用的構(gòu)造方法岗屏,
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);
}
可見最終是調(diào)用
Suite
的protected Suite(Class<?> klass, List<Runner> runners)
構(gòu)造方法構(gòu)建的Suite
辆琅,該參數(shù)中的
List<Runner> runners
是由builder.runners
而來漱办,而由前而的調(diào)用鏈可知,這個(gè)builder是在Computer
類中getSuite
的方法中婉烟,在new Suite
時(shí)直接new
的一個(gè)匿名的RunnerBuilder
該
RunnerBuilder
實(shí)現(xiàn)了runnerForClass
方法娩井,而該方法的具體實(shí)現(xiàn)是由上文(Request
類classes
方法)中AllDefaultPossibilitiesBuilder
來實(shí)現(xiàn)的,該處又是一個(gè)鉤子方法似袁,即當(dāng)調(diào)用這個(gè)Suite
對(duì)象的runnerForClass
方法時(shí)洞辣,實(shí)際調(diào)用的是AllDefaultPossibilitiesBuilder
的runnerForClass
。-
讓我們回到
Suite
的構(gòu)建中昙衅,由剛才的構(gòu)造方法可以看出扬霜,在構(gòu)建Suite
之前先調(diào)用了匿名RunnerBuilder
實(shí)例中的runners
,繼續(xù)看這個(gè)RunnerBuilder.runners
都做了什么public List<Runner> runners(Class<?> parent, Class<?>[] children) throws InitializationError { //添加父class而涉,此時(shí)由Suite調(diào)用過來時(shí)為null addParent(parent); try { //此時(shí)的children是從開始一路傳遞下來的測(cè)試類的class return runners(children); } finally { //移除父class removeParent(parent); } }
繼續(xù)看該方法中調(diào)用的runners
方法
private List<Runner> runners(Class<?>[] children) {
ArrayList<Runner> runners = new ArrayList<Runner>();
for (Class<?> each : children) {
//循環(huán)遍歷所有測(cè)試類的class著瓶,構(gòu)建Runner
//由此可以發(fā)現(xiàn)每個(gè)測(cè)試類對(duì)應(yīng)一個(gè)Runner
Runner childRunner = safeRunnerForClass(each);
if (childRunner != null) {
runners.add(childRunner);
}
}
return runners;
}
繼續(xù)看safeRunnerForClass
方法
public Runner safeRunnerForClass(Class<?> testClass) {
try {
return runnerForClass(testClass);
} catch (Throwable e) {
return new ErrorReportingRunner(testClass, e);
}
}
- 該方法實(shí)際是調(diào)用的
runnerForClass
來得到的Runner
,再看下方法所在類:抽象類RunnerBuilder
方法中 - 還記得我們現(xiàn)在是哪個(gè)環(huán)節(jié)中嘛啼县,正是在
new Suite
的構(gòu)建方法中材原,所以這個(gè)地方的玄機(jī)就在于,該處的RunnerBuilder
是前創(chuàng)建new Suite
時(shí)創(chuàng)建的匿名RunnerBuilder
季眷,而這個(gè)匿名RunnerBuilder
的runnerForClass
正是由Request.classes
方法中new
的AllDefaultPossibilitiesBuilder
對(duì)象來實(shí)現(xiàn)的余蟹。
這就是鉤子方法的妙用,也稱為模板方法
接下來就讓我們看下AllDefaultPossibilitiesBuilder
的runnerForClass
讓我們來看下調(diào)用時(shí)序
3.RunnerBuilder
首先來看上文中調(diào)用的AllDefaultPossibilitiesBuilder.runnerForClass
public Runner runnerForClass(Class<?> testClass) throws Throwable {
//初始化RunnerBuilder列表
//該處將Junit所有的RunnerBuilder都創(chuàng)建好放入集合
//從該處可以以知道該類為什么叫AllDefaultPossibilitiesBuilder了
List<RunnerBuilder> builders= Arrays.asList(
ignoredBuilder(), //IgnoredBuilder需要忽略測(cè)試的RunnerBuilder
annotatedBuilder(), //AnnotatedBuilder帶有注解的RunnerBuilder
suiteMethodBuilder(), //SuiteMethodBuilder
junit3Builder(), //JUnit3Builder(目測(cè)是為了兼容junit3)
junit4Builder()); //JUnit4Builder
for (RunnerBuilder each : builders) {
Runner runner= each.safeRunnerForClass(testClass);
if (runner != null)
return runner;
}
return null;
}
- 由以上代碼可以發(fā)現(xiàn)子刮,在
AllDefaultPossibilitiesBuilder
中威酒,首先把所有RunnerBuilder
都構(gòu)建好 - 然后循環(huán)遍歷,調(diào)用每一個(gè)
builder
的safeRunnerForClass
方法 - 進(jìn)入該方法不難發(fā)現(xiàn)實(shí)際是調(diào)用每一個(gè)builder的
runnerForClass
方法挺峡,只要命中任何一個(gè)builder
的構(gòu)建規(guī)則兼搏,即使用該builder
創(chuàng)建Rnner
,然后退出循環(huán) - 該處應(yīng)用正是一個(gè)責(zé)任鏈模式
讓我們逐個(gè)看看各個(gè)builder
的runnerForClass
-
IgnoredBuilder
:public Runner runnerForClass(Class<?> testClass) { if (testClass.getAnnotation(Ignore.class) != null) return new IgnoredClassRunner(testClass); } return null; }
可見如果測(cè)試類中加上了@Ignore
則會(huì)使用該builder
-
AnnotatedBuilder
:public Runner runnerForClass(Class<?> testClass) throws Exception { //獲取測(cè)試類中用RunWith注解標(biāo)注的類 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 { ////創(chuàng)建RunWith注解中聲明類的實(shí)例沙郭,并將測(cè)試類的class傳入 return runnerClass.getConstructor(Class.class).newInstance( new Object[] { testClass }); } catch (NoSuchMethodException e) { //先忽略異常處理 }
- 由該段代碼可知佛呻,如果測(cè)試中在
@RunWith
注解中指定了Runner
,則使用該builder
病线,并使用反射創(chuàng)建指定的Runner
- 看到此吓著,相信你知道為什么我們?cè)谟肑unit寫單測(cè)的時(shí)候,單測(cè)類上面注解@RunWith的用途了
-
SuiteMethodBuilder
:
進(jìn)入suiteMethodBuilder()
方法protected RunnerBuilder suiteMethodBuilder() { if (fCanUseSuiteMethod) return new SuiteMethodBuilder(); return new NullBuilder(); }
首先我們看到的是不是直接new SuiteMethodBuilder
送挑,而是首先看fCanUseSuiteMethod
屬性是否為true绑莺,還記得該屬性是什么時(shí)候賦值的嘛?返回去看下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
也就是說惕耕,測(cè)試類中如果沒有ignored
和annotated
纺裁,首先使用的RunnerBuilder
實(shí)際是SuiteMethodBuilder
,并執(zhí)行該builder
的runnerForClass
方法,進(jìn)入一探究竟
public Runner runnerForClass(Class<?> each) throws Throwable {
//判斷測(cè)試的class中是否有suite方法
if (hasSuiteMethod(each))
//創(chuàng)建suiteMethod欺缘,該類繼承自JUnit38ClassRunner
return new SuiteMethod(each);
return null;
}
public boolean hasSuiteMethod(Class<?> testClass) {
try {
testClass.getMethod("suite");
} catch (NoSuchMethodException e) {
return false;
}
return true;
}
- 根據(jù)此段代碼可以發(fā)現(xiàn)栋豫,如果測(cè)試類中沒有
suite
方法的話返回的是null
- 此時(shí)在
AllDefaultPossibilitiesBuilder.runnerForClass
中會(huì)繼續(xù)循環(huán)尋找下一下builder
JUnit3Builder
:如果測(cè)試類使用的是Junit3
的測(cè)試方式,則使用該builder
谚殊,?該builder
不再細(xì)看-
JUnit4Builder
:該builder
很簡(jiǎn)單丧鸯,直接new BlockJUnit4ClassRunner
public Runner runnerForClass(Class<?> testClass) throws Throwable { return new BlockJUnit4ClassRunner(testClass); }
- 從以上
AllDefaultPossibilitiesBuilder.runnerForClass
方法的執(zhí)行看來,Junit4
默認(rèn)使用的builder
是BlockJUnit4ClassRunner
讓我們?cè)倩氐轿覀兊闹骶€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);
}
- 由以上流程可知丛肢,在創(chuàng)建
Suite
這個(gè)Runner
時(shí),首先把所有測(cè)試類對(duì)應(yīng)的具體Runner
通過對(duì)應(yīng)的RunnerBuilder
構(gòu)建好剿干,放入Suite
的List<Runner> runners
屬性中蜂怎,至此Suite
構(gòu)建完成(構(gòu)造方法中的super(klass)
后續(xù)再看)
總之,JunitCore
中使用的Runner
是直接new
的Suite
置尔,而這個(gè)Suite
中屬性List<Runner> runners
杠步,默認(rèn)情況下,這些runner
都是BlockJUnit4ClassRunner
至此Runner
的構(gòu)建完成撰洗,讓我來接下來看下整個(gè)Runner
構(gòu)建的時(shí)序圖