Junit4 架構設計系列(2): Runner.run()與Statement

Overall

系列入口:html

Junit4 架構設計系列(1): Request,ClassRequest 和 RunnerBuilder設計模式

前文中,咱們基本理清了Junit4執行Case大致上的Flow:
Request -> ClassRequest.getRunner() -> AllDefaultPossibilitiesBuilder.safeRunnerForClass() -> runner.run()架構

而且介紹了類Request,ClassRequest,和RunnerBuilder,剩下runner.run()沒講,那本文就從這提及。ide

Runner.run()是如何執行Case的?

run()方法是抽象類Runner定義的一個方法,目的就是執行Case。各個Runner的子類都要實現這個方法。ui

/**
 * Run the tests for this runner.
 *
 * @param notifier will be notified of events while tests are being run--tests being
 * started, finishing, and failing
 */
public abstract void run(RunNotifier notifier);

從前文知道,默認負責執行Junit4風格case的Runner是BlockJUnit4ClassRunner, 可是BlockJUnit4ClassRunner並非直接繼承與Runner類,而是中間多了一層ParentRunner<T>, 以下圖:
this

ParentRunner類負責filter 和 sort Test Class, 處理 @BeforeClass and @AfterClass 方法, 和各類 ClassRules, 而且按順序執行Test Class。lua

那咱們來看看ParentRunner是如何執行Junit4風格的Test Class的,具體實現以下:架構設計

@Override
public void run(final RunNotifier notifier) {
    EachTestNotifier testNotifier = new EachTestNotifier(notifier,
            getDescription());
    try {
        Statement statement = classBlock(notifier);
        statement.evaluate();
    } catch (AssumptionViolatedException e) {
        testNotifier.addFailedAssumption(e);
    } catch (StoppedByUserException e) {
        throw e;
    } catch (Throwable e) {
        testNotifier.addFailure(e);
    }
}

第一行是實例化一個EachTestNotifier,主要爲了記錄執行過程的,這裏暫且不作詳細解釋。
很明顯,Try裏的邏輯纔是真正的執行步驟。邏輯也很清晰,就是獲得Statement,而後調用evaluate()方法。設計

繼續跟進方法classBlock,咱們就會看到下面的邏輯:code

protected Statement classBlock(final RunNotifier notifier) {
    Statement statement = childrenInvoker(notifier);
    if (!areAllChildrenIgnored()) {
        statement = withBeforeClasses(statement);
        statement = withAfterClasses(statement);
        statement = withClassRules(statement);
    }
    return statement;
}

這個邏輯是用於組合咱們指望的Statement,不難看出,Runner.run()方法主要涉及下面幾個執行邏輯:

  • childInvoker方法表示要執行執行這條Test Class了
  • withBeforeClasses則是判斷,Test Class有沒有被@BeforeClass修飾的方法?要是有的話就要先執行它
  • withAfterClasses則判斷Test Class有沒有@AfterClass修飾的方法
  • withClassRules這個則是檢測Test Class有沒有適用TestRules

當全部的statement包裝好後,調用statement.evaluate()就能夠按要求,按順序的執行咱們但願的結果了。

看到這種嵌套輸入與輸出的寫法,會不會有中恍然大悟的感受?!這不就是裝飾者模式的經典適用場景嘛。

要注意的是,這裏處理的都是Class,而對於Method級,實際上也有相似裝飾者模式的適用場景, 咱們從上面childInvoker跟進去,最終會發現,真正執行Test Method的是BlockJunit4ClassRunner類,首先它實現了ParentRunner的抽象方法runChild:

//
// Implementation of ParentRunner
//

@Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
    Description description = describeChild(method);
    if (isIgnored(method)) {
        notifier.fireTestIgnored(description);
    } else {
        Statement statement;
        try {
            statement = methodBlock(method);
        }
        catch (Throwable ex) {
            statement = new Fail(ex);
        }
        runLeaf(statement, description, notifier);
    }
}

而後在期方法塊中methodBlock組合statement:

protected Statement methodBlock(final FrameworkMethod method) {
    Object test;
    try {
        test = new ReflectiveCallable() {
            @Override
            protected Object runReflectiveCall() throws Throwable {
                return createTest(method);
            }
        }.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;
}

跟ClassBlock殊途同歸。

詳解Statement

Statement在Junit4中是很是重要的一塊,徹底能夠大書特書,如圖:

在JUnit4中,一切動做均可以用Statement來表示,從上圖包的分配方式咱們能夠看到,Junit4不但預約義了一些必須使用或者使用頻率高的動做,如

  • InvokeMethod 執行Test Method
  • RunBefores 先於InvokeMethod以前執行@Before方法
  • RunAfters 執行@After方法
  • Fail Throw Errors
  • FailOnTime 設置Timeout的入口
  • ExpectException 定義指望所拋出的異常

還定義了RunRules,可以讓咱們Reuse或者是從新定義本身的Rule.可謂方便至極.

設計模式知識補充

Decorator模式

Decorator(裝飾者)模式動態地將責任添加到對象上。在擴展功能方面,裝飾者模式提供了比繼承更有彈性的替代方案。
這是我實現的標準裝飾者模式的類圖:

而裝飾者模式特色就是:

  • 裝飾者和被裝飾者對象有相同的超類型
  • 咱們能夠用多個裝飾者去包裝一個對象
  • 裝飾者能夠在所委託被裝飾者的行爲以前/或以後,就上本身的行爲,以達到特定的目的
  • 對象能夠在運行時動態地,不限量的用裝飾者來裝飾

高亮的第三點很關鍵,咱們知道Junit4之因此能讓 @BeforeClass 和 @AfterClass 等註解,按要求或前或後的執行,就是利用了這一點。

敬請期待

後續計劃

  • Junit4架構設計系列(3) RunNotifer

童鞋,若是以爲本文還算用心,還算有用,何不點個贊呢(⊙o⊙)?

相關文章
相關標籤/搜索