最近对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);
Statement可以看做是一个函数封装(Functional Interface),内部只有一个execute()函数。method是被@Test修饰的测试函数(本例中的test1()),withBefores把SampleClass中被@Before修饰的所有函数找出来,然后封装成一个新的Statement。
//比如说,可以用下面的naive实现
void withBefores(Method m, _, Statement statement) {
// 利用Q1和Q2的知识点把@Before修饰的函数都找出来
List befores = ...
return new Statement{
@Override
public execute() {
for (Method b : befores) {
b.execute();
}
m.execute();
}
}
}
Q4: Q3中的BlockJUnit4ClassRunner和SampleTest搅合到一起的?
A: 请自己去看BlockJUnit4ClassRunner的constructor的parameter是什么。
Summary
利用Java原生的getDeclaredAnnotations和getDeclaredMethods,可以轻松地得到测试类SampleTest中函数的annotations。
JUnit用一个Statement来做把setUp(),test1(),以及tearDown()封装到一起,并保证其执行顺序。
References
下期内容
BlockJUnit4ClassRunner又被谁调用了呢?
运行unit test的入口在哪里?
请看:[深入JUnit] 测试运行的入口