美文网首页TestNG与JUnit
Junit源码阅读笔记二(Runner构建)

Junit源码阅读笔记二(Runner构建)

作者: 春狗 | 来源:发表于2017-08-19 14:37 被阅读44次

1.Runner

上一节讲到了Junit的运行实际上是调用Runner中的run方法执行的,那么接下来总结一下Runner,首先我们看下Runner的类图

Runner

2.Runner的构建

让我们回到类Request.class中的classes方法

public static Request classes(Computer computer, Class<?>... classes) {
    try {
        AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
        Runner suite = computer.getSuite(builder, classes);
        return runner(suite);
    } catch (InitializationError e) {
        throw new RuntimeException(
                "Bug in saff's brain: Suite constructor, called as above, should always complete");
    }
}

由此可知第一个Runner是由computer.getSuite(builder, classes)中获得,参数builder是直接new AllDefaultPossibilitiesBuilder,从名称可以看出,这个builder是所有默认的可能builder,看起来很牛逼的样子,点进去可以发现构造方法只有一个属性赋值,先忽略,继续看computer.getSuite(builder, classes)

public Runner getSuite(final RunnerBuilder builder,
        Class<?>[] classes) throws InitializationError {

    return new Suite(new RunnerBuilder() {
        @Override
        public Runner runnerForClass(Class<?> testClass) throws Throwable {
            return getRunner(builder, testClass);
        }
    }, classes);
}

protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
    return builder.runnerForClass(testClass);
}

由此可见这个Runner是直接new的一个Suite,进入 Suite类看该处调用的构造方法,

public Suite(RunnerBuilder builder, Class<?>[] classes) throws InitializationError {
    this(null, builder.runners(null, classes));
}
protected Suite(Class<?> klass, List<Runner> runners) throws InitializationError {
    super(klass);
    this.runners = Collections.unmodifiableList(runners);
}
  • 可见最终是调用Suiteprotected Suite(Class<?> klass, List<Runner> runners)构造方法构建的Suite

  • 该参数中的List<Runner> runners是由builder.runners而来,而由前而的调用链可知,这个builder是在Computer类中getSuite的方法中,在new Suite时直接new的一个匿名的RunnerBuilder

  • RunnerBuilder实现了runnerForClass方法,而该方法的具体实现是由上文(Requestclasses方法)中AllDefaultPossibilitiesBuilder来实现的,该处又是一个钩子方法,即当调用这个Suite对象的runnerForClass方法时,实际调用的是AllDefaultPossibilitiesBuilderrunnerForClass

  • 让我们回到Suite的构建中,由刚才的构造方法可以看出,在构建Suite之前先调用了匿名RunnerBuilder实例中的runners,继续看这个RunnerBuilder.runners都做了什么

      public List<Runner> runners(Class<?> parent, Class<?>[] children)
          throws InitializationError {
          //添加父class,此时由Suite调用过来时为null
          addParent(parent);
          try {
              //此时的children是从开始一路传递下来的测试类的class
              return runners(children);
          } finally {
              //移除父class
              removeParent(parent);
          }
      }
    

继续看该方法中调用的runners方法

 private List<Runner> runners(Class<?>[] children) {
    ArrayList<Runner> runners = new ArrayList<Runner>();
    for (Class<?> each : children) {
        //循环遍历所有测试类的class,构建Runner
        //由此可以发现每个测试类对应一个Runner
        Runner childRunner = safeRunnerForClass(each);
        if (childRunner != null) {
            runners.add(childRunner);
        }
    }
    return runners;
}

继续看safeRunnerForClass方法

public Runner safeRunnerForClass(Class<?> testClass) {
    try {
        return runnerForClass(testClass);
    } catch (Throwable e) {
        return new ErrorReportingRunner(testClass, e);
    }
}
  • 该方法实际是调用的runnerForClass来得到的Runner,再看下方法所在类:抽象类RunnerBuilder方法中
  • 还记得我们现在是哪个环节中嘛,正是在new Suite的构建方法中,所以这个地方的玄机就在于,该处的RunnerBuilder是前创建new Suite时创建的匿名RunnerBuilder,而这个匿名RunnerBuilderrunnerForClass正是由Request.classes方法中newAllDefaultPossibilitiesBuilder对象来实现的。
    这就是钩子方法的妙用,也称为模板方法
    接下来就让我们看下AllDefaultPossibilitiesBuilderrunnerForClass
    让我们来看下调用时序
Suite创建时Builder的调用

3.RunnerBuilder

首先来看上文中调用的AllDefaultPossibilitiesBuilder.runnerForClass

public Runner runnerForClass(Class<?> testClass) throws Throwable {
            //初始化RunnerBuilder列表
            //该处将Junit所有的RunnerBuilder都创建好放入集合
            //从该处可以以知道该类为什么叫AllDefaultPossibilitiesBuilder了

    List<RunnerBuilder> builders= Arrays.asList(
            ignoredBuilder(),  //IgnoredBuilder需要忽略测试的RunnerBuilder
            annotatedBuilder(),  //AnnotatedBuilder带有注解的RunnerBuilder
            suiteMethodBuilder(),  //SuiteMethodBuilder
            junit3Builder(),  //JUnit3Builder(目测是为了兼容junit3)
            junit4Builder());  //JUnit4Builder

    for (RunnerBuilder each : builders) {
        Runner runner= each.safeRunnerForClass(testClass);
        if (runner != null)
            return runner;
    }
    return null;
}
  • 由以上代码可以发现,在AllDefaultPossibilitiesBuilder中,首先把所有RunnerBuilder都构建好
  • 然后循环遍历,调用每一个buildersafeRunnerForClass方法
  • 进入该方法不难发现实际是调用每一个builder的runnerForClass方法,只要命中任何一个builder的构建规则,即使用该builder创建Rnner,然后退出循环
  • 该处应用正是一个责任链模式

让我们逐个看看各个builderrunnerForClass

  1. IgnoredBuilder:

     public Runner runnerForClass(Class<?> testClass) {
         if (testClass.getAnnotation(Ignore.class) != null)
             return new IgnoredClassRunner(testClass);
         }
         return null;
     }
    

可见如果测试类中加上了@Ignore则会使用该builder

  1. AnnotatedBuilder

     public Runner runnerForClass(Class<?> testClass) throws Exception {
         //获取测试类中用RunWith注解标注的类
         RunWith annotation= testClass.getAnnotation(RunWith.class);
         if (annotation != null)
             return buildRunner(annotation.value(), testClass);
         return null;
      }
    
     public Runner buildRunner(Class<? extends Runner> runnerClass,
         Class<?> testClass) throws Exception {
         try {
             ////创建RunWith注解中声明类的实例,并将测试类的class传入
             return runnerClass.getConstructor(Class.class).newInstance(
                 new Object[] { testClass });
         } catch (NoSuchMethodException e) {
             //先忽略异常处理
     }
    
  • 由该段代码可知,如果测试中在@RunWith注解中指定了Runner,则使用该builder,并使用反射创建指定的Runner
  • 看到此,相信你知道为什么我们在用Junit写单测的时候,单测类上面注解@RunWith的用途了
  1. SuiteMethodBuilder
    进入suiteMethodBuilder()方法

     protected RunnerBuilder suiteMethodBuilder() {
         if (fCanUseSuiteMethod)
             return new SuiteMethodBuilder();
         return new NullBuilder();
     }
    

首先我们看到的是不是直接new SuiteMethodBuilder,而是首先看fCanUseSuiteMethod属性是否为true,还记得该属性是什么时候赋值的嘛?返回去看下Request.classes方法

public static Request classes(Computer computer, Class<?>... classes) {
    try {
        AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
        Runner suite = computer.getSuite(builder, classes);
        return runner(suite);
    } catch (InitializationError e) {
        throw new RuntimeException(
                "Bug in saff's brain: Suite constructor, called as above, should always complete");
    }
}

正是new AllDefaultPossibilitiesBuilder(true); 中的true
也就是说,测试类中如果没有ignoredannotated,首先使用的RunnerBuilder实际是SuiteMethodBuilder,并执行该builderrunnerForClass方法,进入一探究竟

public Runner runnerForClass(Class<?> each) throws Throwable {
    //判断测试的class中是否有suite方法
    if (hasSuiteMethod(each))
        //创建suiteMethod,该类继承自JUnit38ClassRunner
        return new SuiteMethod(each);
    return null;
}

public boolean hasSuiteMethod(Class<?> testClass) {
    try {
        testClass.getMethod("suite");
    } catch (NoSuchMethodException e) {
        return false;
    }
    return true;
}
  • 根据此段代码可以发现,如果测试类中没有suite方法的话返回的是null
  • 此时在 AllDefaultPossibilitiesBuilder.runnerForClass中会继续循环寻找下一下builder
  1. JUnit3Builder:如果测试类使用的是Junit3的测试方式,则使用该builder,�该builder不再细看

  2. JUnit4Builder:该builder很简单,直接new BlockJUnit4ClassRunner

     public Runner runnerForClass(Class<?> testClass) throws Throwable {
         return new BlockJUnit4ClassRunner(testClass);
     }
    
  • 从以上AllDefaultPossibilitiesBuilder.runnerForClass方法的执行看来,Junit4默认使用的builderBlockJUnit4ClassRunner

让我们再回到我们的主线new Suite中,

public Suite(RunnerBuilder builder, Class<?>[] classes) throws InitializationError {
    this(null, builder.runners(null, classes));
}
protected Suite(Class<?> klass, List<Runner> runners) throws InitializationError {
    super(klass);
    //Collections.unmodifiableList将传入的List变得不可修改
    this.runners = Collections.unmodifiableList(runners);
}
  • 由以上流程可知,在创建Suite这个Runner时,首先把所有测试类对应的具体Runner通过对应的RunnerBuilder构建好,放入SuiteList<Runner> runners属性中,至此Suite构建完成(构造方法中的super(klass)后续再看)

总之,JunitCore中使用的Runner是直接newSuite,而这个Suite中属性List<Runner> runners,默认情况下,这些runner都是BlockJUnit4ClassRunner
至此Runner的构建完成,让我来接下来看下整个Runner构建的时序图

4.Runner构建时序图

Runner构建时序图

相关文章

网友评论

    本文标题:Junit源码阅读笔记二(Runner构建)

    本文链接:https://www.haomeiwen.com/subject/lirorxtx.html