欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 宝鼎售后问题提交 | 后台管理


新闻资讯

MENU

软件开发知识
原文出处: saymagic

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有一个劈头相识,我画了一个类图:

有大概是 <a href=劳务调派系统RunBefores" src="http://www.importnew.com/https:/blog.saymagic.cn/pic/understand-junit/o_1atjj1na31qbrjujamk2ku1na19.png" />

上图中仅是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);
    }

    可以看到,整个结构函数大抵都在做一些验证和初始化的事情,需要引起我们留意的应该是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), methodsForAnnotations);
                }
                // ensuring fields are sorted to make sure that entries are inserted
                // and read from fieldForAnnotations in a deterministic order
                for (Field eachField : getSortedDeclaredFields(eachClass)) {
                    addToAnnotationLists(new FrameworkField(eachField), fieldsForAnnotations);
                }
          }
    }