设为首页 加入收藏

TOP

JUnit 源码解析(一)
2018-01-30 12:42:55 】 浏览:496
Tags:JUnit 源码 解析

JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架,以Eclipse、IDEA等为代表的Java开发环境都对JUnit提供了非常友善的支持。提到Erich Gamma,他就是大名鼎鼎的设计模式:可复用面向对象软件的基础》一书的作者之一。因此,JUnit当中的设计模式的运用相当得当,所以,JUnit的源码可谓相当优良的一本武林秘籍,非常值得一看。 本文基于JUnit4.12,将从JUnit的运行流程,Match验证,两个方面,来对JUnit的源码进行整体的分析。

运行流程

JUnit的启动方式有很多,比如在Android Studio中我们可以直接点击某个被@Test注解的函数来运行:

/pic/understand-junit/o_1atjgr81913guppi80nerk17s09.jpeg

此时,启动的是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 是一个抽象类,其 eva luate()函数代表着在测试中将被执行的方法。
  • 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);
}

可以看到,整个构造函数大致都在做一些验证和初始化的工作,需要引起我们注意的应该是scanAnnotatedMembers方法:

 protected void scanAnnotatedMembers(Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations, Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations) {
       for (Class<?> eachClass : getSuperClasses(clazz)) {
            for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) {
                addToAnnotationLists(new FrameworkMethod(eachMethod),
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇JUnit 源码解析 下一篇理解 Consistent Hashing

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目