【原创】Junit4详解二:Junit4 Runner以及test case执行顺序和源代码理解

概要:

前一篇文章我们总体介绍了Junit4的用法以及一些简单的测试。之前我有个疑惑,Junit4怎么把一个test case跑起来的,在test case之前和之后我们能做些什么?

Junit4执行顺序是怎样的?带着这些问题,我写了这篇文章,仅供参考,不对之处,盼留言指正,感激万分。前一篇文章:【原创】Junit4详解一:Junit总体介绍

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构造的时候就会开始验证。另外一个比较重要的方法是:

methodBlock(FrameworkMethod method)

method里面包含当前要测试的方法。

这个方法的作用验证方法能否执行,然后把当前测试类的信息(当前类,测试的方法)传给InvokeMethod,以待后续测试方法的执行,接着获取当前类的元数据信息,保存起来。

2. ParentRunner:Junit4测试执行器的基类,它提供了一个测试器所需要的大部分功能。继承它的类需要实现:

protected abstract List<T> getChildren();

protected abstract Description describeChild(T child);

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/ 淘宝搜小矮人鞋坊,主营精致美丽时尚女鞋,为您的白雪公主挑一双哦。谢谢各位博友的支持。

 

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

 

======================    以上分析仅代表个人观点,欢迎指正与交流   =========================

 

======================    草原战狼博客,转载请注明出处,万分感谢   =========================

 

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

 

 

 

 

posted @ 2014-01-27 14:30  草原战狼  阅读(16637)  评论(2编辑  收藏  举报
草原战狼淘宝小店

No one indebted for others,while many people don't know how to cherish others.

No one indebted for others,while many people don't know how to cherish others.

Don‘t cry because it is over, smile because it happened.

Don‘t try so hard, the best things come when you least expect them to.