一直以来都很好奇Junit是怎么让代码跑起来的,是按照常规思路:先创建对象,然后再调用其方法吗?趁着这个节日现在刚好分析记录一下。
示例代码
先下一个简单的单元测试类:
package com.xx.lib;
import org.junit.Test;
public class JunitDemo {
// 测试demo代码
@Test
public void test01() {
System.out.println("000");
}
}
执行结果:
junit执行入口代码定位
如果要分析代码的执行入口,我们可以在需要执行的方法里加一个断点,然后通过debug的方式执行查看其调用栈信息;
效果如下:
由于是通过idea执行的单元测试,该单元测试的启动会由idea的插件执行(即JunitStarter)
真正Junit框架的入口是:JunitCore这个类
JunitCore
首先看看类结构:
初步看:这个类竟然有一个main方法,各种重载的run方法,感觉有戏;
直接给出结论:
测试用例是使用JUnitCore 类来执行的,JUnitCore 是运行测试的外观类。
要从命令行运行测试,可以运行java org.junit.runner.JUnitCore。
对于只有一次的测试运行,可以使用静态方法 runClasses(Class[])。
runClasses方法
我们先来使用一下runClasses这个方法:
org.junit.runner.JUnitCore#runClasses(java.lang.Class<?>...)
我们编写一个类来验证一下,代码如下:
package com.xx.lib;
import org.junit.Test;
import org.junit.runner.JUnitCore;
public class MyTest {
public static void main(String[] args) {
JUnitCore.runClasses(MyTest.class);
}
@Test
public void test01() {
System.out.println("666");
}
}
执行结果如下,成功第执行了我们标有@Test注解的测试方法:
接下来我们来分析一下runClasses这个方法执行流程:
public static Result runClasses(Class<?>... classes) {
return runClasses(defaultComputer(), classes);
}
defaultComputer()方法返回了一个 org.junit.runner.Computer 对象
↓
public static Result runClasses(Computer computer, Class<?>... classes) {
return new JUnitCore().run(computer, classes);
}
JUnitCore就是当前类,我们继续跟踪方法
↓
public Result run(Computer computer, Class<?>... classes) {
return run(Request.classes(computer, classes));
}
org.junit.runner.Request#classes(org.junit.runner.Computer, java.lang.Class<?>…)是静态方法,该方法返回的是一个 org.junit.runner.Request 对象;
该方法里会生成一个org.junit.runner.Runner对象,并会与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");
}
}
使用了AllDefaultPossibilitiesBuilder 对象,生成一个Runner 对象并与Request 进行绑定;即runner方法,代码如下:
public static Request runner(final Runner runner) {
return new Request() {
@Override
public Runner getRunner() {
return runner;
}
};
}
创建一个Request对象,并重写getRunner方法;
↓
public Result run(Request request) {
return run(request.getRunner());
}
代码回到JUnitCore中,继续执行run方法(参数就是上一步绑定的runner,即computer.getSuite(builder, classes)的返回值)
↓
最终调用的方法是:
public Result run(Runner runner) {
Result result = new Result();
RunListener listener = result.createListener();
notifier.addFirstListener(listener);
try {
notifier.fireTestRunStarted(runner.getDescription());
runner.run(notifier);
notifier.fireTestRunFinished(result);
} finally {
removeListener(listener);
}
return result;
}
notifier是一个 org.junit.runner.notification.RunNotifier 实例,
runner是上面说的computer.getSuite(builder, classes)方法生成的对象,我们来分析一下:
1、computer是 org.junit.runner.Computer对象
2、builder是 org.junit.internal.builders.AllDefaultPossibilitiesBuilder对象
通过进一步源码分析:
runner是 org.junit.internal.builders.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;
}