这里比较好理解,被打了 @Test 注解的方法,一定是 Junit 通过某种方式将其扫描到了,然后作为待执行的一个集合或者队列中。下面通过分析代码来论证下。
org.junit.runners.BlockJUnit4ClassRunner#getChildren
@Override
protected List<FrameworkMethod> getChildren() {
return computeTestMethods();
}
通过方法 computeTestMethods 方法名其实就可以看出其目的,就是计算出所有的测试方法。
etAnnotatedMethods 通过指定的 annotationClass 类型,将当前 TestClass 中类型为 annotationClass 类型注解标注的方法过滤出来。
getFilteredChildren 中最后将获取得到的测试方法放在 filteredChildren 中缓存起来。这里简单汇总下 @Test 注解被识别的整个过程(其他注解如 @Before 都是一样的)。
1、Junit 在初始化构建 Runner 的过程,内部会基于给定的 测试类创建一个 TestClass 对象模型,用于描述当前测试类在 Junit 中的表示。
// clazz 是待测试类
public TestClass(Class<?> clazz) {
this.clazz = clazz;
if (clazz != null && clazz.getConstructors().length > 1) {
// 测试类不能有有参构造函数
throw new IllegalArgumentException(
"Test class can only have one constructor");
}
Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations =
new LinkedHashMap<Class<? extends Annotation>, List<FrameworkMethod>>();
Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations =
new LinkedHashMap<Class<? extends Annotation>, List<FrameworkField>>();
// 扫描待测试类中所有的 Junit 注解,包括 @Test @Before @After 等等
scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations);
// 过滤出打在方法上的注解,
this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations);
// 过滤出打在变量上的注解
this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations);
}
methodsForAnnotations 和 fieldsForAnnotations 缓存了当前待测试类所有被 junit 注解标注过的方法和变量。
2、getFilteredChildren 中,从 methodsForAnnotations 中筛选出所有 @Test 注解标注的方法。(getDescription()-> getFilteredChildren -> computeTestMethods -> 从 methodsForAnnotations 按类型过滤)。
3、返回所有 @Test 注解标注的方法。
Before 和 After 执行时机
要搞定这个问题,其实有必要了解下 Junit 中一个比较重要的概念 Statement。
public abstract class Statement {
/**
* Run the action, throwing a {@code Throwable} if anything goes wrong.
*/
public abstract void evaluate() throws Throwable;
}
Statement 从 junit 4.5 版本被提出,Statement 表示在运行 JUnit 测试组件的过程中要在运行时执行的一个或多个操作,简单说就是,对于被 @Before @After 注解标注的方法,在 JUnit 会被作为一种 Statement 存在,分别对应于 RunBefores 和 RunnerAfter,这些 statement 中持有了当前运行所有的 FrameworkMethod。
FrameworkMethod 是 JUnit 中所有被 junit 注解标注方式的内部描述,@Test, @Before, @After, @BeforeClass, @AfterClass 标注的方法最终都作为 FrameworkMethod 实例存在。
Statement 的创建有两种方式,基于 FrameworkMethod 的 methodBlock 和基于 RunNotifier 的 classBlock,这里介绍 methodBlock ,classBlock 下节讨论。
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);
statement = withInterruptIsolation(statement);
return statement;
}
withAfters、withBefores 会将 RunAfters 和 RunBefore 绑定到 statement,最后 形成一个 statement 链,这个链的执行入口时 RunAfters#evaluate。
@Override
public void evaluate() throws Throwable {
List<Throwable> errors = new ArrayList<Throwable>();
try {
next.evaluate();
} catch (Throwable e) {
errors.add(e);
} finally {
// 在 finally 中执行 after 方法
for (FrameworkMethod each : afters) {
try {
invokeMethod(each);
} catch (Throwable e) {
errors.add(e);
}
}
}
MultipleFailureException.assertEmpty(errors);
}
next 链中包括 before 和待执行的测试方法。
所以我们看到的就是 before -> testMethod -> after。
这里其实和预想的不太一样,关于 before 和 after 这种逻辑,第一想法是通过代理的方式,对测试方法进行代理拦截,类似 Spring AOP 中的 Before 和 After,其实不然。
BeforeClass 和 AfterClass 执行时机
前面分析了 methodBlock,了解到 junit 中通过这个方法创建 statement 并且将 before 和 after 的方法绑定给 statement,以此推断,classBlock 的作用就是将 BeforeClass 和 AfterClass 绑定给statement 。
protected Statement classBlock(final RunNotifier notifier) {
// childrenInvoker 这里会调用到 methodBlock
Statement statement = childrenInvoker(notifier);
if (!areAllChildrenIgnored()) {
statement = withBeforeClasses(statement);
statement = withAfterClasses(statement);
statement = withClassRules(statement);
statement = withInterruptIsolation(statement);
}
return statement;
}
BeforeClass 和 before 都会对应创建一个 RunnerBefores,区别在于 BeforeClass 在创建 RunnerBefores 时,不会指定目标测试方法。
· BeforeClass 在执行 statement 之前,运行该类和超类上所有非覆盖的@BeforeClass方法;如果有抛出异常,停止执行并传递异常。
· AfterClass 在执行 statement链最后,在该类和超类上运行所有未覆盖的 @AfterClass 方法;始终执行所有 AfterClass 方法:如有必要,将前面步骤抛出的异常与来自 AfterClass 方法的异常合并到 org.junit.runners.model.MultipleFailureException 中。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理