1

最近对JUnit和Mockito的内部实现比较感兴趣,将在接下来的一段时间,和大家一起深入代码细节。

王侯将相,宁有种乎 (JUnit也没啥Magic吧)

阅读前提

  • 听说过Java Annotation

  • 使用过JUnit

  • 知道@Before, @After, @Test

  • 对JUnit的内部实现有兴趣

代码版本: junit 4.12
代码搜索工具: http://grepcode.com/
常用符号

  • _: 用来略去代码段中无关紧要的parameter

  • ...: 用来略去无关紧要的代码实现

Example

下面是一个很简单的JUunit Test Class

public class SampleTest {
   @Before
   protected void setUp(){ ... }

   @Test
   public void test1(){ ... }       
   
   @After
   public void tearDown(){ ... }
}

本文要解答的问题:@Before, @Test, @After如何影响test workflow的?

Q&A

Q1. 如何提取一个函数的Annotation信息?
A: 任何Java提供了Method::getDeclaredAnnotations()

Q2. 如何把SampleTest里的methods都罗列出来?
A: Java提供了Class::getDeclaredMethods()

Q3: @Before, @Test, @After的执行顺序如何保证的?
A: 在junit的BlockJUnit4ClassRunner class中有一段代码:

Statement statement = methodInvoker(method, _);
statement = withBefores(method, _, statement);
statement = withAfters(method, _, statement);

http://grepcode.com/file/repo1.maven.org...

Statement可以看做是一个函数封装(Functional Interface),内部只有一个execute()函数。method是被@Test修饰的测试函数(本例中的test1()),withBeforesSampleClass中被@Before修饰的所有函数找出来,然后封装成一个新的Statement

//比如说,可以用下面的naive实现
void withBefores(Method m, _, Statement statement) {
  // 利用Q1和Q2的知识点把@Before修饰的函数都找出来
  List<Method> befores = ...
  return new Statement{
    @Override
    public execute() {
      for (Method b : befores) {
        b.execute();
      }
      m.execute();
    }
  }
}

Q4: Q3中的BlockJUnit4ClassRunnerSampleTest搅合到一起的?
A: 请自己去看BlockJUnit4ClassRunner的constructor的parameter是什么。

Summary

  • 利用Java原生的getDeclaredAnnotationsgetDeclaredMethods,可以轻松地得到测试类SampleTest中函数的annotations。

  • JUnit用一个Statement来做把setUp()test1(),以及tearDown()封装到一起,并保证其执行顺序。

References

下期内容

BlockJUnit4ClassRunner又被谁调用了呢?
运行unit test的入口在哪里?
请看:[深入JUnit] 测试运行的入口
clipboard.png


衣衣
141 声望4 粉丝

工程师