Junit测试用例是怎么执行的

521 阅读4分钟

Junit测试用例均是以JunitCore.run(Class<?> class)开始的,接下来分析一下,Junit是怎么触发用例执行的

1.获取Runner

public Result run(Class<?>... classes) {
        return run(defaultComputer(), classes);
}

入参为Class对象,代表承载测试用例的测试类,继续分析run(defaultComputer(), classes),重点分析一下Computer.class,其代码如下

public class Computer {
15    /**
16     * Returns a new default computer, which runs tests in serial order
17     */
18    public static Computer serial() {
19        return new Computer();
20    }
21
22    /**
23     * Create a suite for {@code classes}, building Runners with {@code builder}.
24     * Throws an InitializationError if Runner construction fails
25     */
26    public Runner getSuite(final RunnerBuilder builder,
27            Class<?>[] classes) throws InitializationError {
28        return new Suite(new RunnerBuilder() {
29            @Override
30            public Runner runnerForClass(Class<?> testClass) throws Throwable {
31                return getRunner(builder, testClass);
32            }
33        }, classes);
34    }
35
36    /**
37     * Create a single-class runner for {@code testClass}, using {@code builder}
38     */
39    protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
40        return builder.runnerForClass(testClass);
41    }
42}

此类比较简单,包括三个函数

  1. serial():静态接口,反馈Computer对象,功能类似构造函数
  2. getSuite():获取指定测试类集合的Runner信息,suite代码测试类集合,一个测试类集合中包含多个测试类
  3. getRunner():获取指定测试类的Runner

由此可见,此类的主要功能是获取测试用例的Runner,这三个函数在此不详细介绍,在下边的章节进行分析

public Result run(Computer computer, Class<?>... classes) {
       return run(Request.classes(computer, classes));
   }

接着嵌套调用run(Request request),继续分析Request类

junit/src/main/java/org/junit/runner/Request.java
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");
        }
    }

通过静态方法classes构造Request对象,那classes干了什么呢?

AllDefaultPossibilitiesBuilder

首先,构造RunnerBuilder对象,从名字可以看出,这个对象是用例生成runner的,AllDefaultPossibilitiesBuilder是

RunnerBuilder的子类,RunnerBuilder是个抽象类,其规定了解析Runner的一些基本规则,其子类需实现

runnerForClass方法,那我们具体看一下AllDefaultPossibilitiesBuilder做了什么

public class AllDefaultPossibilitiesBuilder extends RunnerBuilder {
10    private final boolean canUseSuiteMethod;
11
12    public AllDefaultPossibilitiesBuilder(boolean canUseSuiteMethod) {
13        this.canUseSuiteMethod = canUseSuiteMethod;
14    }
15
16    @Override
17    public Runner runnerForClass(Class<?> testClass) throws Throwable {
18        List<RunnerBuilder> builders = Arrays.asList(
19                ignoredBuilder(),
20                annotatedBuilder(),
21                suiteMethodBuilder(),
22                junit3Builder(),
23                junit4Builder());
24
25        for (RunnerBuilder each : builders) {
26            Runner runner = each.safeRunnerForClass(testClass);
27            if (runner != null) {
28                return runner;
29            }
30        }
31        return null;
32    }
33
34    protected JUnit4Builder junit4Builder() {
35        return new JUnit4Builder();
36    }
37
38    protected JUnit3Builder junit3Builder() {
39        return new JUnit3Builder();
40    }
41
42    protected AnnotatedBuilder annotatedBuilder() {
43        return new AnnotatedBuilder(this);
44    }
45
46    protected IgnoredBuilder ignoredBuilder() {
47        return new IgnoredBuilder();
48    }
49
50    protected RunnerBuilder suiteMethodBuilder() {
51        if (canUseSuiteMethod) {
52            return new SuiteMethodBuilder();
53        }
54        return new NullBuilder();
55    }
56}

AllDefaultPossibilitiesBuilder的构造函数很简单,只是透传了canUseSuiteMethod,其代码是否要查找SuiteMethod,我们重点看一下runnerForClass,如何找到合适的Runner。runnerForClass首先,建立了一个数组,其中包含5和RunnerBuilder,分别为:IgnoredBuilder:查找测试类否有Ignore注解AnnotatedBuilder:解析测试类的RunWith注解,生成Runner,此Runner可以是Junit自带的,也可以是自定义的,会在后续文章中介绍如何自定义RunerSuiteMethodBuilder:查找测试类中是否有SuiteRunnerJUnit3Builder:如果是使用Junit4之前的语法编写的测试类,返回JUnit38ClassRunnerJUnit4Builder:返回BlockJUnit4ClassRunner,即Junit4默认的Runner然后,遍历Runner列表,找到合适的Runner后就返回Computer.getSuite在此详细分析一下getSuite干了什么

public Runner getSuite(final RunnerBuilder builder,
27            Class<?>[] classes) throws InitializationError {
28        return new Suite(new RunnerBuilder() {
29            @Override
30            public Runner runnerForClass(Class<?> testClass) throws Throwable {
31                return getRunner(builder, testClass);
32            }
33        }, classes);
34    }

Suite的构造函数有两个入参,分别是RunnerBuilder和Class,Class无需多讲,重点看RunnerBuilder是如何构造的。

new 一个RunnerBuilder,并实现了runnerForClass,调用Computer.getRunner(RunnerBuilder, Class),在此能接上上边分析的AllDefaultPossibilitiesBuilder.runnerForClass,获取所有测试类的Runner集合。

至此,已经获取到了执行用例的Runner,接下来就是执行用例了

执行测试用例

public Result run(Runner runner) {
132        Result result = new Result();//实例化Result类,用于保存用例执行结果
133        RunListener listener = result.createListener();//新建执行监听器,用于监听用例执行的状态,如pass、fail、ignore
134        notifier.addFirstListener(listener);//设置监听器
135        try {
136            notifier.fireTestRunStarted(runner.getDescription());//开始执行用例
137            runner.run(notifier);//执行用例
138            notifier.fireTestRunFinished(result);//保存结果
139        } finally {
140            removeListener(listener);
141        }
142        return result;
143    }

在后续自定义Runner介绍的时候,再分析run的流程