大家好,前些天分享了一些Juint源码阅读心得,具体内容参考Junit源码阅读心得(1),其中主要分享了Junit中的framework包中的核心接口和类的作用,今天主要要和大家一起分享一些Junit入口的知识点。好了,闲话少说,我们来进入正题。
我们首先来分析第一个核心类,也是Junit开始测试的启动类:JunitCore,这个类位于org.junit.runner包中。
在JunitCore中,主要有两种启动方式,分为main方法启动方式和runClasses()方法启动方式,不过两个方法最后都调用到了JunitCore的run()方法中:
//main方法启动
public static void main(String... args) {
runMainAndExit(new RealSystem(), args);
}
//runClasses方法启动,该方法存在重载,具体源码请大家自行阅读
public static Result runClasses(Class<?>... classes) {
return new JUnitCore().run(defaultComputer(), classes);
}
//两种启动方式的最终调用,同样存在重载,源码大家自行阅读
public Result run(Computer computer, Class<?>... classes) {
return run(Request.classes(computer, classes));
}
在这里我们只介绍主要的runClasses()方法启动过程。
由于在runClass方法中,调用了run()方法,我们来看一下run()方法的具体实现:
public Result run(Computer computer, Class<?>... classes) {
return run(Request.classes(computer, classes));
}
在这里由于computer对象完全是new出来的一个默认对象,所以我们暂时忽略,我们看到在这里调用了Request中的classes()方法来获取一个Request类型的请求对象:
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");
}
}
在这里我们创建了一个Builder对象,这个对象主要用来构建Runner,而Runner则是负责运行测试,也就是说Builder负责根据不同的条件来创建不同的测试运行器。这里的builder为默认的AllDefaultPossibilitiesBuilder类型。之后我们将builder作为入参调用computer的getSuite()方法,在这个方法中,我们将会根据不同的条件逐步创建出我们需要的Runner。具体内容如下:
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);
}
/**
* Create a single-class runner for {@code testClass}, using {@code builder}
*/
protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
return builder.runnerForClass(testClass);
}
这里的代码逻辑就比较清楚了,我们返回了一个Suite对象,这个对象可以根据传入的参数Buidler对象,调用Budiler对象的runnerForClass()方法,获取相应的Runner,由于我们这里使用的Builder为AllDefaultPossibilitiesBuilder类型的对象,所以也就自然调用了这个类型中的runnerForClass()方法:
public Runner runnerForClass(Class<?> testClass) throws Throwable {
List<RunnerBuilder> builders= Arrays.asList(
ignoredBuilder(),
annotatedBuilder(),
suiteMethodBuilder(),
junit3Builder(),
junit4Builder());
for (RunnerBuilder each : builders) {
Runner runner= each.safeRunnerForClass(testClass);
if (runner != null)
return runner;
}
return null;
}
protected JUnit4Builder junit4Builder() {
return new JUnit4Builder();
}
protected JUnit3Builder junit3Builder() {
return new JUnit3Builder();
}
protected AnnotatedBuilder annotatedBuilder() {
return new AnnotatedBuilder(this);
}
protected IgnoredBuilder ignoredBuilder() {
return new IgnoredBuilder();
}
protected RunnerBuilder suiteMethodBuilder() {
if (fCanUseSuiteMethod)
return new SuiteMethodBuilder();
return new NullBuilder();
}
在这里我们获取了所有可用的RunnerBuilder对象,然后使用这些对象来创建合适的Runner,当我们的测试类中没有任何的类注解时,那么默认就会返回JUnit4Builder锁创建的Runner:
public Runner runnerForClass(Class<?> testClass) throws Throwable {
return new BlockJUnit4ClassRunner(testClass);
}
也就是我们在第一篇博客中提到的默认Runner:BlockJUnit4ClassRunner。
然后获取Runner的过程就已经结束,我们回到Request中的classes()方法中,我们看到,当获取到合适的Runner后,我们将Runner作为入参调用了runner()方法,然后将Runner封装在了一哥Request中,并返回Request对象:
public static Request runner(final Runner runner) {
return new Request(){
@Override
public Runner getRunner() {
return runner;
}
};
}
这样,就获取到了Request,然后返回到了最开始的JunitCore类中的run方法中,然后继续调用重载的run()方法:
public Result run(Runner runner) {
Result result= new Result();
RunListener listener= result.createListener();
fNotifier.addFirstListener(listener);
try {
fNotifier.fireTestRunStarted(runner.getDescription());
runner.run(fNotifier);
fNotifier.fireTestRunFinished(result);
} finally {
removeListener(listener);
}
return result;
}
在这里,我们使用了观察者模式,注册了监听器,然后调用Runner的run()方法,开始进行测试。
在前面我们知道这里使用的runner为默认的BlockJUnit4ClassRunner类型,在这个类中找不到run()方法,原因是BlockJUnit4ClassRunner继承了ParentRunner类,而run()方法就在ParentRunner中:
public void run(final RunNotifier notifier) {
EachTestNotifier testNotifier= new EachTestNotifier(notifier,
getDescription());
try {
Statement statement= classBlock(notifier);
statement.evaluate();
} catch (AssumptionViolatedException e) {
testNotifier.fireTestIgnored();
} catch (StoppedByUserException e) {
throw e;
} catch (Throwable e) {
testNotifier.addFailure(e);
}
}
在这里我们首先获取了代表要运行的测试封装对象Statement,在这里Statement表示一个或多个要被执行的测试方法,其中只有一个方法,用来执行测试。我们看一下Statement对象的获取过程:
//方法1
protected Statement classBlock(final RunNotifier notifier) {
Statement statement= childrenInvoker(notifier);
statement= withBeforeClasses(statement);
statement= withAfterClasses(statement);
statement= withClassRules(statement);
return statement;
}
//方法2
protected Statement childrenInvoker(final RunNotifier notifier) {
return new Statement() {
@Override
public void evaluate() {
runChildren(notifier);
}
};
}
//方法3
private void runChildren(final RunNotifier notifier) {
for (final T each : getFilteredChildren())
fScheduler.schedule(new Runnable() {
public void run() {
ParentRunner.this.runChild(each, notifier);
}
});
fScheduler.finished();
}
//方法4
private List<T> getFilteredChildren() {
if (fFilteredChildren == null)
fFilteredChildren = new ArrayList<T>(getChildren());
return fFilteredChildren;
}
我们这里主要说明上面四个方法,首先在classBlock()方法中我们通过执行childrenInvoker()重写了Statement对象的执行方法evaluate(),那么重写的内容是什么呢,我们需要看runChildren()的实现,在这个方法中我们首先通过getFilteredChildren()获取了一个List,那么这个List中的内容是什么呢?在这里又调用了getChildren(),这个方法是个模板方法,由子类实现,还记得我们是怎么进入ParentRunner的吗,是的,是通过BlockJUnit4ClassRunner,那么这个方法的实现就是在BlockJUnit4ClassRunner中:
@Override
protected List<FrameworkMethod> getChildren() {
return computeTestMethods();
}
protected List<FrameworkMethod> computeTestMethods() {
return getTestClass().getAnnotatedMethods(Test.class);
}
在这个方法中返回了List类型,代表被@Test注解的需要执行的测试方法,这样我们就找到了需要被执行的方法,就可以开始测试了。我们再回到ParentRunner的runChildren(),在我们获取到测试方法后,我们使用fScheduler对象依次调用runChild()方法,同样,这也是一个模板方法,方法实现还是在BlockJUnit4ClassRunner中:
@Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description= describeChild(method);
if (method.getAnnotation(Ignore.class) != null) {
notifier.fireTestIgnored(description);
} else {
runLeaf(methodBlock(method), description, notifier);
}
}
在这里主要的代码是runLeaf(methodBlock(method), description, notifier);
这一句,在这里methodBlock()负责构造Statement对象,runLeaf()负责具体执行:
protected Statement methodBlock(FrameworkMethod method) {
Object test;
try {
test= new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest();
}
}.run();
} catch (Throwable e) {
return new Fail(e);
}
Statement statement= methodInvoker(method, test);
statement= possiblyExpectingExceptions(method, test, statement);
statement= withPotentialTimeout(method, test, statement);
statement= withBefores(method, test, statement);
statement= withAfters(method, test, statement);
statement= withRules(method, test, statement);
return statement;
}
/**
* Returns a {@link Statement} that invokes {@code method} on {@code test}
*/
protected Statement methodInvoker(FrameworkMethod method, Object test) {
return new InvokeMethod(method, test);
}
public class InvokeMethod extends Statement {
private final FrameworkMethod fTestMethod;
private Object fTarget;
public InvokeMethod(FrameworkMethod testMethod, Object target) {
fTestMethod= testMethod;
fTarget= target;
}
@Override
public void evaluate() throws Throwable {
fTestMethod.invokeExplosively(fTarget);
}
}
public Object invokeExplosively(final Object target, final Object... params)
throws Throwable {
return new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return fMethod.invoke(target, params);
}
}.run();
}
我们可以看到这里有一个InvokeMethod类继承自Statement类,其中封装了要执行的测试方法以及监听。其中的evaluate()方法调用了测试方法的invokeExplosively(),然后测试方法便得到了执行。我们再一次回到BlockJUnit4ClassRunner的methodBlock(),在这里,我们获取到Statement对象之后,又分别获取了被@After,@Before,@Rule,@Test()中的expected,timeout注解的各个方法以及属性,并进行封装,这个过程形成了一个链状结构,虽然返回的对象都是Statement,但是方法中其实返回的是Statement的子类:ExpectException,FailOnTimeout,RunBefores,RunAfters,RunRules。
具体内容请大家自行参考源码。然后我们看一下runLeaf()方法:
protected final void runLeaf(Statement statement, Description description,
RunNotifier notifier) {
EachTestNotifier eachNotifier= new EachTestNotifier(notifier, description);
eachNotifier.fireTestStarted();
try {
statement.evaluate();
} catch (AssumptionViolatedException e) {
eachNotifier.addFailedAssumption(e);
} catch (Throwable e) {
eachNotifier.addFailure(e);
} finally {
eachNotifier.fireTestFinished();
}
}
这里的代码就很简单了,就仅仅调用了我们之前说过的被重载后的statement.evaluate();
方法,这样测试方法就可以得到了执行。现在我们再一次回到ParentRunner的classBlock()方法中,到这里我们已经获取了一个接近完整的Statement对象,然后在classBlock()方法中,我们继续对注解有@BeforeClass,@AfterClass以及@ClassRule的部分进行封装,过程与之前的@After注解封装大体一致,这样子Statement就已经封装完了所有的需要执行的注解,然后将Statement对象返回,在ParentRunner中最终得到运行。
好了,今天就分享到这里,希望能够对大家有所帮助,如有不足之处,还请多多指教。