JUnit4 源码 之 基础模块

JUnit4 源码 之 基础模块

今天突发奇想 想看看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
    • [Class]Rule – 当使用与Method和Field上时,分别对应不同的接口:MethodRule、TestRule。不过目前MethodRule接口只有一个实现类:TestWatchman,并且该实现类已经被标记为Deprecated。
  • 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

其他略过

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章