JUnit源码解析
发表于:2016-10-04来源:saymagic作者:saymagic点击数:
标签:junit
JUnit源码解析JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架,以Eclipse、IDEA等为代表的Java开发环境都对JUnit提供了非常友善的支持。提到Erich Gamma,他就是大名鼎鼎的《设计模式
JUnit源码解析
JUnit是由 Erich Gamma 和 Kent Beck 编写的一个
回归测试框架,以Eclipse、IDEA等为代表的
Java开发环境都对JUnit提供了非常友善的支持。提到Erich Gamma,他就是大名鼎鼎的《设计模式:可复用面向对象软件的基础》一书的作者之一。因此,JUnit当中的设计模式的运用相当得当,所以,JUnit的源码可谓相当优良的一本武林秘籍,非常值得一看。 本文基于JUnit4.12,将从JUnit的运行流程,Match验证,两个方面,来对JUnit的源码进行整体的分析。
运行流程
JUnit的启动方式有很多,比如在Android Studio中我们可以直接点击某个被@Test注解的函数来运行:
此时,启动的是JUniteStarter,该类是intellij为我们提供的。感兴趣可以查看其源码: https://github.com/JetBrains/intellij-community/blob/master/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.
java
如果我们使用gradle, 可以执行gradle test运行测试,实际上是在一个线程中执行SuiteTestClassProcessor的processTestClass方法来进行启动。其源码可以查看https://github.com/gradle/gradle/blob/master/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/SuiteTestClassProcessor.java
如上两种都是第三方工具为我们提供的便捷方式,实际上JUnit也提供了一个名为JUnitCore的类来供我们方便的运行
测试用例。
尽管启动JUnit的方式有很多,但这都是打开与JUnit对话的一些方式,最终执行的还是JUnit当中的起到核心作用的一些类,为了让大家对这些核心boss有一个初步了解,我画了一个类图:
上图中仅是JUnit中的几个核心的类,也是本分主要分析的对象。这里先给出一些对象的职责,可以有个大体的了解,后面会通过代码就会更清楚每个对象是如何完成这些职责的:
在类图的中央,有个叫做ParentRunne的对象很引人注目,它继承自Runner.
Runner则表示着JUnit对整个测试的抽象
Runner实现了Describable接口,Describable接口中唯一的函数getDescription()返回了Description对象,记录着测试的信息。
Statement 是一个抽象类,其 evaluate()函数代表着在测试中将被执行的方法。
ParentRunner 共有两个子类,BlockJUnit4ClassRunner 用来运行单个测试类,Suite用来一起运行多个测试类
RunnerBuilder 是生产Runner的策略,如使用@RunWith(Suite.class)标注的类需要使用Suite, 被@Ignore标注的类需要使用IgnoreClassRunner。
TestClass是对被测试的类的封装
综上,我们先从ParentRunner看起,其构造函数如下:
protected ParentRunner(Class<?> testClass) throws InitializationError {
this.testClass = createTestClass(testClass);
validate();
}
this.testClass即前文所说的TestClass,我们进入createTestClass方法来查看其如何将class对象转换为TestClass。
protected TestClass createTestClass(Class<?> testClass) {
return new TestClass(testClass);
}
并没什么东西,具体的逻辑都写在TestClass的内部:
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>>();
scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations);
this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations);