JUnit 源码 之 基础模块
程序员文章站
2022-04-19 23:09:46
...
JUnit 源码 之 基础模块
今天突发奇想 想看看JUnit的实现,于是就翻阅了JUnit的官网和GitHub Project,并初步有所了解,就此进行简单源码记录。
- JUnitCore
- RunnerBuilder
- Statement
- annotation
- Runner
JUnitCore 运行入口
- main方法 一般作为命令行入口
- static runClasses(…) 一般用于手动入口(基本不这么用)Result result = JUnitCore.runClasses(AddPencilsTest.class);
- run(…) IDE集成入口
// from JUnitCore
public Result run(Computer computer, Class<?>... classes)
{ return run(Request.classes(computer, classes)); }
// from Request
public static Request classes(Computer computer, Class<?>... classes) {
// 通过Builder进行构建Runner对象(下面重点介绍)
AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
Runner suite = computer.getSuite(builder, classes);
return runner(suite);
}
// from JUnitCore
public Result run(Runner runner) {
Result result = new Result();
RunListener listener = result.createListener();
notifier.addFirstListener(listener);
try {
notifier.fireTestRunStarted(runner.getDescription());
runner.run(notifier); // 启动单元测试,其他部分均为Listener相关内容
notifier.fireTestRunFinished(result);
} finally { removeListener(listener); }
return result;
}
RunnerBuilder 测试类的包装运行类
- Ignore[Class]Builder – 标记为@Ignore的Test类
- AnnotatedBuilder – 标记为@RunWith的Test类
- JUnit4Builder – 普通类
- AllDefaultPossibilitiesBuilder – 所有RunnerBuilder的通用工厂类
// AllDefaultPossibilitiesBuilder
@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable {
List<RunnerBuilder> builders = Arrays.asList(
ignoredBuilder(), // @Ignore
annotatedBuilder(), // @RunWith
suiteMethodBuilder(), // method name == suite
junit3Builder(),
junit4Builder()); // other (no annotation Test class)
for (RunnerBuilder each : builders) {
Runner runner = each.safeRunnerForClass(testClass);
if (runner != null) { return runner; }
}
return null;
}
// from AnnotatedBuilder
@Override
public Runner runnerForClass(Class<?> testClass) throws Exception {
for (Class<?> currentTestClass = testClass; currentTestClass != null;
currentTestClass = getEnclosingClassForNonStaticMemberClass(currentTestClass)) {
RunWith annotation = currentTestClass.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 {
return runnerClass.getConstructor(Class.class).newInstance(testClass);
} catch (NoSuchMethodException e) {
try {
return runnerClass.getConstructor(Class.class,
RunnerBuilder.class).newInstance(testClass, suiteBuilder);
} catch (NoSuchMethodException e2) {
String simpleName = runnerClass.getSimpleName();
throw new InitializationError(String.format(
CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName));
}
}
}
Statement (各测试方法的对应封装类)
- RunAfters
- RunBefores
- InvokeMethod
- FailOnTimeout
// from RunAfters
@Override
public void evaluate() throws Throwable {
List<Throwable> errors = new ArrayList<Throwable>();
try { next.evaluate(); } catch (Throwable e) { errors.add(e); }
finally {
for (FrameworkMethod each : afters)
{ try { each.invokeExplosively(target); } catch (Throwable e) { errors.add(e); }}
}
MultipleFailureException.assertEmpty(errors);
}
// from RunBefores
@Override
public void evaluate() throws Throwable {
for (FrameworkMethod before : befores) { before.invokeExplosively(target); }
next.evaluate();
}
// from InvokeMethod
@Override
public void evaluate() throws Throwable { testMethod.invokeExplosively(target); }
// from FailOnTimeout
@Override
public void evaluate() throws Throwable {
CallableStatement callable = new CallableStatement();
FutureTask<Throwable> task = new FutureTask<Throwable>(callable);
threadGroup = new ThreadGroup("FailOnTimeoutGroup");
Thread thread = new Thread(threadGroup, task, "Time-limited test");
thread.setDaemon(true);
thread.start();
callable.awaitStarted();
Throwable throwable = getResult(task, thread);
if (throwable != null) {
throw throwable;
}
}
private Throwable getResult(FutureTask<Throwable> task, Thread thread) {
try {
if (timeout > 0) { return task.get(timeout, timeUnit); }
else { return task.get(); }
} catch (InterruptedException e) {
return e; // caller will re-throw; no need to call Thread.interrupt()
} catch (ExecutionException e) {
// test failed; have caller re-throw the exception thrown by the test
return e.getCause();
} catch (TimeoutException e) {
return createTimeoutException(thread);
}
}
private class CallableStatement implements Callable<Throwable> {
private final CountDownLatch startLatch = new CountDownLatch(1);
public Throwable call() throws Exception {
try { startLatch.countDown(); originalStatement.evaluate(); }
catch (Exception e) { throw e; }
catch (Throwable e) { return e; }
return null;
}
public void awaitStarted() throws InterruptedException { startLatch.await(); }
}
annotation
- Base – 核心使用部分,不与介绍
- BeforeClass | Before | Test | After | AfterClass | Ignore
- Advanced
- RunWith – 使用指定Runner来对测试用例进行测试(主要是用于 环境部署 及 分类组装 等)
-
Suite & Parameterized 这两个为JUnit自带用于RunWith注解中的 运行器 (本人暂且这么称呼)
- Suite 配合 SuiteClasses 一起使用,用于整合多个测试用例一起运行测试
- Parameterized 当需要测试某功能在不同参数下的运行情况时,可以使用该 运行器 来提供参数集
- 具体核心代码逻辑 在下篇中讲解
Runner
用于包装一个Test类,与Test Method的对应关系为 Test Method: Runner = M : 1
可以这么描述,一般都会使用ParentRunner抽象类,其中有一个属性为children,该属性中包裹着Test Method
简单铺垫一下Runner的常用实现类的继承关系,以便于下篇文章讲解
- ParentRunner --> Suite --> Parameterized
- ParentRunner --> BlockJUnit4ClassRunner --> BlockJUnit4ClassRunnerWithParameters