系列入口:html
Junit4 架構設計系列(1): Request,ClassRequest 和 RunnerBuilder設計模式
前文中,咱們基本理清了Junit4執行Case大致上的Flow:
Request -> ClassRequest.getRunner() -> AllDefaultPossibilitiesBuilder.safeRunnerForClass() -> runner.run()架構
而且介紹了類Request,ClassRequest,和RunnerBuilder,剩下runner.run()沒講,那本文就從這提及。ide
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()方法主要涉及下面幾個執行邏輯:
當全部的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在Junit4中是很是重要的一塊,徹底能夠大書特書,如圖:
在JUnit4中,一切動做均可以用Statement來表示,從上圖包的分配方式咱們能夠看到,Junit4不但預約義了一些必須使用或者使用頻率高的動做,如
還定義了RunRules,可以讓咱們Reuse或者是從新定義本身的Rule.可謂方便至極.
Decorator(裝飾者)模式動態地將責任添加到對象上。在擴展功能方面,裝飾者模式提供了比繼承更有彈性的替代方案。
這是我實現的標準裝飾者模式的類圖:
而裝飾者模式特色就是:
高亮的第三點很關鍵,咱們知道Junit4之因此能讓 @BeforeClass 和 @AfterClass 等註解,按要求或前或後的執行,就是利用了這一點。
後續計劃