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);
    this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations);

原文转自:http://blog.saymagic.cn/2016/09/30/understand-Junit.html