Junit源码Runner流程图以及运行流程

第二次读junit源码,撸了个流程图,哎也算能看出点门道了,之前脑子都是晕的

总体来说,Junit单元测试的入口在JunitCore里面,通过Core调用run(Runner runner)方法走到ParentRunner(实际上一般是其继承类BlockJUnit4ClassRunner),整个流程我觉得最核心的就是Statement的传递,相当于责任链模式,比较重要的两个方法呢,就是classBlock和methodBlock两个方法。

执行的顺序是先调用classBlock,在classBlock里面再通过childrenInvoker来调用methodBlock

    @Override
    public void run(final RunNotifier notifier) {
        EachTestNotifier testNotifier = new EachTestNotifier(notifier,
                getDescription());
        testNotifier.fireTestSuiteStarted();
        try {
            Statement statement = classBlock(notifier);
            statement.evaluate();
        } catch (AssumptionViolatedException e) {
            testNotifier.addFailedAssumption(e);
        } catch (StoppedByUserException e) {
            throw e;
        } catch (Throwable e) {
            testNotifier.addFailure(e);
        } finally {
            testNotifier.fireTestSuiteFinished();
        }
    }

所以我们首先看classBlock

protected Statement classBlock(final RunNotifier notifier) {
        Statement statement = childrenInvoker(notifier);
        if (!areAllChildrenIgnored()) {
            statement = withBeforeClasses(statement);
            statement = withAfterClasses(statement);
            statement = withClassRules(statement);
            statement = withInterruptIsolation(statement);
        }
        return statement;
}

从上图的逻辑我们可以推断出,@BerforeClass在@Before前面面执行,因为withBeforeClass()方法在methodBlock后面,而越后面的statement越先被回调到,所以先执行@BeforeClass相关的代码。childrenInvoker暂时不管,先看后面的withBeforeClass,可以看到,如果要测试的类有BeforeClass注解,那么会返回一个RunBefores

protected Statement withBeforeClasses(Statement statement) {
        List<FrameworkMethod> befores = testClass
                .getAnnotatedMethods(BeforeClass.class);
        return befores.isEmpty() ? statement :
                new RunBefores(statement, befores, null);
    }

再看RunBefores的代码:

public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) {
        this.next = next;
        this.befores = befores;
        this.target = target;
    }

    @Override
    public void evaluate() throws Throwable {
        for (FrameworkMethod before : befores) {
            invokeMethod(before);
        }
        next.evaluate();
    }

这个就相当于生成一个statement链表,再依次执行statement的evaluate方法,其他的withAfterClasses,withClassRules也是类似,withInterruptIsolation这个主要是用来处理中断的,但是为啥要处理中断,我也不知道,,,

然后再回到classBlock的childrenInvoker方法:

protected Statement childrenInvoker(final RunNotifier notifier) {
        return new Statement() {
            @Override
            public void evaluate() {
                runChildren(notifier);
            }
        };
    }

再点进runChildren方法:

private void runChildren(final RunNotifier notifier) {
        final RunnerScheduler currentScheduler = scheduler;
        try {
            for (final T each : getFilteredChildren()) {
                currentScheduler.schedule(new Runnable() {
                    public void run() {
                        ParentRunner.this.runChild(each, notifier);
                    }
                });
            }
        } finally {
            currentScheduler.finished();
        }
    }

点进runChild:


    @Override
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
        Description description = describeChild(method);
        if (isIgnored(method)) {
            notifier.fireTestIgnored(description);
        } else {
            Statement statement = new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    methodBlock(method).evaluate();
                }
            };
            runLeaf(statement, description, notifier);
        }
    }

就能看到我们的methodBlock方法了:

protected Statement methodBlock(final FrameworkMethod method) {
        Object test;
        try {
            test = new ReflectiveCallable() {
                @Override
                protected Object runReflectiveCall() throws Throwable {
                    return createTest(method);
                }
            }.run();
        } catch (Throwable e) {
            return new Fail(e);
        }

        Statement statement = methodInvoker(method, test);
        statement = possiblyExpectingExceptions(method, test, statement);
        statement = withPotentialTimeout(method, test, statement);
        statement = withBefores(method, test, statement);
        statement = withAfters(method, test, statement);
        statement = withRules(method, test, statement);
        statement = withInterruptIsolation(statement);
        return statement;
    }

createTest()我的理解就是通过构造器加载当前测试的类,比如当前要测试的是HelloTest,拿我们就通过createTest()创建一个HelloTest实例(newInstance)

而methodInvoke(method,test)其实是一个回调函数,这个函数在BlockJUnit4ClassRunner就是返回一个new InvokeMethod,但是在TheoryAnchor里面有个被覆写的BlockJUnit4ClassRunner类,这个覆写类其实是返回一个 methodCompletesWithParameters方法,具体细节就不说了

继续看possiblyExpectingExceptions,这个其实就是对应@Test(expected = xxException)的用法,用过的都懂,withPotentialTimeout就是@Test(timeout= xx),withBefores对应@Before,后面都差不多,不过以上这些,都没有实际调用evaluate方法,只是定义了很多关于Statement evaluate回调函数,而真正的调用其实是在runChild方法的最后一行代码,runLeaf中:

/**
     * Runs a {@link Statement} that represents a leaf (aka atomic) test.
     */
    protected final void runLeaf(Statement statement, Description description,
            RunNotifier notifier) {
        EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
        eachNotifier.fireTestStarted();
        try {
            statement.evaluate();
        } catch (AssumptionViolatedException e) {
            eachNotifier.addFailedAssumption(e);
        } catch (Throwable e) {
            eachNotifier.addFailure(e);
        } finally {
            eachNotifier.fireTestFinished();
        }
    }

这里面的evaluate会触发一系列的回调函数,首先是触发withInterruptIsolation的evaluate函数,因为它是最后一个声明的,然后触发withRules,依次类推,直到最后触发methodInvoker的statment的,这个statement会调用FrameworkMethod的invokeExplosively,即通过反射的方式,正式走进@Test标注的方法,开始执行测试

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值