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

其他略過

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