【原創】Junit4詳解二:Junit4 Runner以及test case執行順序和源代碼理解

概要:

前一篇文章咱們整體介紹了Junit4的用法以及一些簡單的測試。以前我有個疑惑,Junit4怎麼把一個test case跑起來的,在test case以前和以後咱們能作些什麼?html

Junit4執行順序是怎樣的?帶着這些問題,我寫了這篇文章,僅供參考,不對之處,盼留言指正,感激萬分。前一篇文章:【原創】Junit4詳解一:Junit整體介紹java

Junit4 runner整體介紹

Junit4編譯器在執行TestCase的過程當中利用反射機制,以便咱們能夠對測試的開始過程當中進行一些預處理,如讀取元數據信息,攔截異常,數據庫操做等,因爲Junit4默認的測試執行器是:BlockJUnit4ClassRunner,咱們以這個執行器粗略地作一些研究。在TestCase執行過程當中,主要用到如下類,BlockJUnit4ClassRunner,ParentRunner,Statement,TestRule,Description,RunNotifier,InvokeMethod.如下簡單作些解釋。數據庫

Junit4默認重要類簡述,助於理解源代碼

1. BlockJUnit4ClassRunner:Junit4的默認測試執行器,它有以前版本的runner一樣的行爲,也就兼容了以前的runner。可是它是基於Statement,實現更加簡單,容許用戶在執行工做流中某個合適的點插入用戶新的操做,基於這個runner的繼承和重用都是能夠的,這樣能更加具備靈活性。它繼承ParentRunner,Junit4要求,執行器的構造函數要把測試類傳進來。這個類的實現了ParentRunner的runChild(真正執行測試的方法,每一個測試方法都會執行runChild),describeChild(FrameworkMethod method)(用來獲取測試類的元數據信息,以及方法和類的信息),一些驗證的方法,這些驗證方法在ParentRunner構造的時候就會開始驗證。另一個比較重要的方法是:app

methodBlock(FrameworkMethod method)ide

method裏面包含當前要測試的方法。函數

這個方法的做用驗證方法可否執行,而後把當前測試類的信息(當前類,測試的方法)傳給InvokeMethod,以待後續測試方法的執行,接着獲取當前類的元數據信息,保存起來。post

2. ParentRunner:Junit4測試執行器的基類,它提供了一個測試器所須要的大部分功能。繼承它的類須要實現:測試

protected abstract List<T> getChildren();ui

protected abstract Description describeChild(T child);this

protected abstract void runChild(T child, RunNotifier notifier);

3. Statement:在運行期時,執行test case前能夠插入一些用戶動做,它就是描述這些動做的一個類。繼承這個類要實現:

    /**

     * Run the action, throwing a {@code Throwable} if anything goes wrong.

     */

     public abstract void evaluate() throws Throwable;

 這個方法,這個方法會前後在ParentRunner.run()和ParentRunner.runLeaf()這兩個方法裏面調用。另外,咱們能夠自定義一個Statement,而且實現evaluate()方法。

4. TestRule:TestRule能夠描述一個或多個測試方法如何運行和報告信息的接口。在TestRule中能夠額外加入一些check,咱們可讓一個test case失敗/成功,也能夠加入一些setup和cleanup要作的事,也能夠加入一些log之類的報告信息。總之,跑test case以前的任何事,均可以在裏面作。須要實現apply()方法。

    /**

     * Modifies the method-running {@link Statement} to implement this

     * test-running rule.

     * @param base The {@link Statement} to be modified

     * @param description A {@link Description} of the test implemented in {@code base}

     * @return a new statement, which may be the same as {@code base},

     * a wrapper around {@code base}, or a completely new Statement.

     */

     Statement apply(Statement base, Description description);

 這兩個類的用法能夠見後面的綜合例子。

5. Description:存儲着當前單個或多個test case的描述信息。這些信息跟邏輯不關,好比元數據信息等。實例化Description用Description.createTestDescription()方法。

 

6. RunNotifier:運行時通知器。執行Runner.run(RunNotifier runNotifier)方法時,須要傳一個RunNotifier進去,這個RunNotifier是事件的管理器,它能幫助咱們監控測試執行的狀況。

 

7. InvokeMethod:最終執行test case裏面的測試方法經過這個類來作,這個類會間接調用Method.invoke()方法通知編譯器執行@test方法。

Junit4啓動test case到結束整個過程概述

  1. Junit4Builder編譯器會構造Test運行器,如BlockJUnit4ClassRunner,BlockJUnit4ClassRunner 會經過本身的構造器,把當前測試類傳到runner裏。
  2. 運行ParentRunner.run()方法,run 方法會獲取測試類的Description信息,Description信息會包含測試類的信息,而後執行classBlock(RunNotifier),這個方法獲取Statement信息,首先構造一個childrenInvoker,而後在Statement的evaluate()方法調用runChildren()方法,用來真正地執行test方法,這個步驟會等到測試真正執行後開始作。如今是先獲取Statement會處理三種註解,@Before,@After,@Rule,把標註這些註解的方法分別放在集合裏,以便後面可以處理這些方法。
  3. 準備工做都作好以後,會執行步驟2裏從classBlock(RunNotifier)獲取到的Statement的evaluate()方法,這個方法用來對Statement來講是開了一個口,用戶能夠自定義Statement的方法,不過在這裏,evaluate()主要是來執行步驟2調用的runChildren()方法。
  4. runChildren()方法的做用是:遍歷測試類的全部測試方法(getFilteredChildren),開啓線程調度fScheduler,調度線程給每個測試方法,而後執行Runnable.run()方法,讓測試執行器,讓測試執行器能夠執行測試類的測試方法(runChild)。
  5. 執行測試方法首先會判斷方法是否含有@Ignore註解,若是有,那麼忽略它的測試。若是沒有那麼執行runLeaf()方法。這個方法是真正咱們執行測試的開始。
  6. RunLeaf(Statement, Description, RunNotifier),首先Statement是有methodBlock(FrameworkMethod)產生,FrameworkMethod 存着當前要執行的測試方法,在這個方法裏,用了反射機制。這時候Junit4會構造RunRules會把Statement, Description apply()到MethodRules, TestRule進去,也就是說,咱們在外部構造的帶有@Rule,@Before, @After等就在這裏執行,如:
    @Rule
    public final TestRule testRule = new CommonRule();

     此時,咱們就能夠在咱們新構建的CommonRule類裏面,的apply()方法,作一些對test的預處理,好比預處理鏈接數據庫字符串,讀取方法上元數據的信息等;

  7. 而後就真正執行RunLeaf(Statement, Description, RunNotifier),經過傳進來的參數構建EachTestNotifier,執行fireTestStarted,而後,再打開對話,statement.evaluate(),這個執行過程當中,會到InvokeMethod類,而後調用InvokeMethod.evaluate(),最後調用Method.invoke()方法真正地執行了test實體。測試開始了。

備註:就如剛所說的,真正開始測試前,咱們能夠用apply()進行一些處理,一樣地,咱們能夠在apply()方法中,建立用戶Statement,這樣就能在evaluate()方法中,作一些操做。如執行腳本,日誌等。

元數據的執行順序

1. 獲取元數據信息的順序

@BeforeClass -> @AfterClass -> ClassRule -> @Test(拿元數據裏的expect Exception) -> @Test(拿元數據裏的timeout信息) -> @Before -> @After -> @Rule,

2. 註解所標註的方法執行順序

@ClassRule(TestRule.apply()) -> @BeforeClass -> @Rule(TestRule.apply())  -> @Before -> @Test(test method1) ->@After -> if existed @Rule, then Statement.evaluate() -> @Rule(TestRule.apply()) -> @Before -> @Test(test method2) -> @After -> if existed @Rule, then Statement.evaluate() … -> @AfterClass -> if existed @ClassRule, then Statement.evaluate()

經過Statement.evaluate()執行他們的方法實體,最終執行測試方法的主體。

附錄:

TestRule Statement以及註解標識的方法執行順序代碼示例

 

  1 package com.citi.risk.services.credit.facility.impl;
  2 
  3 import java.io.Closeable;
  4 import java.io.IOException;
  5 
  6 import org.junit.After;
  7 import org.junit.AfterClass;
  8 import org.junit.Before;
  9 import org.junit.BeforeClass;
 10 import org.junit.ClassRule;
 11 import org.junit.Rule;
 12 import org.junit.Test;
 13 import org.junit.rules.ExpectedException;
 14 import org.junit.rules.TestRule;
 15 import org.junit.runner.Description;
 16 import org.junit.runners.model.Statement;
 17 
 18 /**
 19  * 執行順序以下: 默認test方法的執行順序是隨機的,沒有順序
 20  * @ClassRule(TestRule.apply()) -> @BeforeClass -> @Rule(TestRule.apply())  -> @Before 
 21  * -> @Test(test method1) ->@After -> if existed @Rule, then Statement.evaluate() 
 22  * -> @Rule(TestRule.apply()) -> @Before -> @Test(test method2) -> @After 
 23  * -> if existed @Rule, then Statement.evaluate() … -> @AfterClass 
 24  * -> if existed @ClassRule, then Statement.evaluate()
 25  * @author 草原戰狼
 26  *
 27  */
 28 public class TestClass {
 29 
 30     @Rule
 31     public ExpectedException expectedException = ExpectedException.none();
 32     
 33     @Rule
 34     public TestRule testRule = new TestRuleValueImpl();
 35     
 36     @Rule
 37     public TestRule testRuleMethod() {
 38         System.out.println();
 39         System.out.println("@Rule Method");
 40         return new TestRuleMethodImpl();
 41     }
 42     @ClassRule
 43     public static TestRule testClassRuleMethod() {
 44         System.out.println("@ClassRule Method");
 45         return new TestRuleMethodImpl();
 46     }
 47 
 48     static class TestRuleValueImpl implements TestRule{
 49         @Override
 50         public Statement apply(Statement base, Description description) {
 51             System.out.println("@Rule property--TestRuleValueImpl execute apply()");
 52             return new StatementValueImpl(base);
 53         }
 54     }
 55 
 56     static class StatementValueImpl extends Statement {
 57         Statement base;
 58         StatementValueImpl(Statement base) {
 59             this.base = base;
 60         }
 61         @Override
 62         public void evaluate() throws Throwable {
 63             System.out.println("@Rule property--StatementValueImpl execute evaluate()");
 64             base.evaluate();
 65         }
 66         
 67     }
 68     static class TestRuleMethodImpl implements TestRule{
 69         @Override
 70         public Statement apply(Statement base, Description description) {
 71             System.out.println("@Rule method--TestRuleMethodImpl execute apply()");
 72             return new StatementMethodImpl(base);
 73         }
 74         
 75     }
 76     
 77     static class StatementMethodImpl extends Statement {
 78         Statement base;
 79         StatementMethodImpl(Statement base) {
 80             this.base = base;
 81         }
 82         @Override
 83         public void evaluate() throws Throwable {
 84             System.out.println("@Rule Method--StatementMethodImpl execute evaluate()");
 85             base.evaluate();
 86         }
 87         
 88     }
 89     static class ExpensiveManagedResource implements Closeable {
 90         @Override
 91         public void close() throws IOException {
 92         }
 93     }
 94 
 95     static class ManagedResource implements Closeable {
 96         @Override
 97         public void close() throws IOException {
 98         }
 99     }
100 
101     @BeforeClass
102     public static void setUpClass() {
103         System.out.println("@BeforeClass setUpClass");
104         myExpensiveManagedResource = new ExpensiveManagedResource();
105     }
106 
107     @AfterClass
108     public static void tearDownClass() throws IOException {
109         System.out.println("@AfterClass tearDownClass");
110         myExpensiveManagedResource.close();
111         myExpensiveManagedResource = null;
112     }
113 
114     private ManagedResource myManagedResource;
115     private static ExpensiveManagedResource myExpensiveManagedResource;
116 
117     private void println(String string) {
118         System.out.println(string);
119     }
120 
121     @Before
122     public void setUp() {
123         this.println("@Before setUp");
124         this.myManagedResource = new ManagedResource();
125     }
126 
127     @After
128     public void tearDown() throws IOException {
129         this.println("@After tearDown");
130         this.myManagedResource.close();
131         this.myManagedResource = null;
132         this.println("   ");
133     }
134 
135     @Test
136     public void test1() {
137         this.println("   @Test test1() begin");
138         this.println("   @Test test1() execute during evaluate()");
139         this.println("   @Test test1() finished");
140     }
141 
142     @Test
143     public void test2() {
144         this.println("   @Test test2() begin");
145         this.println("   @Test test2() execute during evaluate()");
146         this.println("   @Test test2() finished");
147     }
148 
149     @Test
150     public void test3() {
151         this.println("   @Test test3() begin");
152         String hi = "   @Test test3() execute during evaluate()";
153         expectedException.expect(Exception.class);
154         expectedException.expectMessage("ddd");
155         this.println(hi);
156         this.println("   @Test test3() finished.");
157     }
158 }
View TestClass Code

  執行的結果以下:

 

 1 @ClassRule Method  2 @Rule method--TestRuleMethodImpl execute apply()  3 @Rule Method--StatementMethodImpl execute evaluate()  4 @BeforeClass setUpClass // 預備期結束  5  // 第一個測試方法開始到結束
 6 @Rule Method 7 @Rule method--TestRuleMethodImpl execute apply() 8 @Rule property--TestRuleValueImpl execute apply() 9 @Rule property--StatementValueImpl execute evaluate() 10 @Rule Method--StatementMethodImpl execute evaluate() 11 @Before setUp 12 @Test test1() begin 13 @Test test1() execute during evaluate() 14 @Test test1() finished 15 @After tearDown 16    
17 // 第二個方法開始到結束,咱們能夠在apply() 和 evaluate()這兩個方法作一些操做。
18 @Rule Method 19 @Rule method--TestRuleMethodImpl execute apply() 20 @Rule property--TestRuleValueImpl execute apply() 21 @Rule property--StatementValueImpl execute evaluate() 22 @Rule Method--StatementMethodImpl execute evaluate() 23 @Before setUp 24  @Test test2() begin 25  @Test test2() execute during evaluate() 26  @Test test2() finished 27 @After tearDown 28    
29 // 第三個方法,這三個方法執行的順序是隨機的,固然Junit4提供了某些排序方式能夠處理
30 @Rule Method 31 @Rule method--TestRuleMethodImpl execute apply() 32 @Rule property--TestRuleValueImpl execute apply() 33 @Rule property--StatementValueImpl execute evaluate() 34 @Rule Method--StatementMethodImpl execute evaluate() 35 @Before setUp 36 @Test test3() begin 37 @Test test3() execute during evaluate() 38 @Test test3() finished. 39 @After tearDown 40    
41 @AfterClass tearDownClass

 草原戰狼淘寶小店:http://xarxf.taobao.com/ 淘寶搜小矮人鞋坊,主營精緻美麗時尚女鞋,爲您的白雪公主挑一雙哦。謝謝各位博友的支持。

 

====================================================================================

 

======================    以上分析僅表明我的觀點,歡迎指正與交流   =========================

 

======================    草原戰狼博客,轉載請註明出處,萬分感謝   =========================

 

====================================================================================

相關文章
相關標籤/搜索