Junit源码阅读心得(2)

本文主要探讨JunitCore的启动方式,特别是runClasses()方法的执行流程,包括如何创建Runner、构建Suite以及执行测试。文章详细分析了从Request构建到Runner的生成,再到最终测试执行的整个过程,涉及BlockJUnit4ClassRunner及其父类ParentRunner中的关键方法。
摘要由CSDN通过智能技术生成

大家好,前些天分享了一些Juint源码阅读心得,具体内容参考Junit源码阅读心得(1),其中主要分享了Junit中的framework包中的核心接口和类的作用,今天主要要和大家一起分享一些Junit入口的知识点。好了,闲话少说,我们来进入正题。
我们首先来分析第一个核心类,也是Junit开始测试的启动类:JunitCore,这个类位于org.junit.runner包中。
在JunitCore中,主要有两种启动方式,分为main方法启动方式和runClasses()方法启动方式,不过两个方法最后都调用到了JunitCore的run()方法中:

    //main方法启动
    public static void main(String... args) {
        runMainAndExit(new RealSystem(), args);
    }
    //runClasses方法启动,该方法存在重载,具体源码请大家自行阅读
    public static Result runClasses(Class<?>... classes) {
        return new JUnitCore().run(defaultComputer(), classes);
    }
    //两种启动方式的最终调用,同样存在重载,源码大家自行阅读
    public Result run(Computer computer, Class<?>... classes) {
        return run(Request.classes(computer, classes));
    }

在这里我们只介绍主要的runClasses()方法启动过程。
由于在runClass方法中,调用了run()方法,我们来看一下run()方法的具体实现:

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

在这里由于computer对象完全是new出来的一个默认对象,所以我们暂时忽略,我们看到在这里调用了Request中的classes()方法来获取一个Request类型的请求对象:

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");
        }
    }

在这里我们创建了一个Builder对象,这个对象主要用来构建Runner,而Runner则是负责运行测试,也就是说Builder负责根据不同的条件来创建不同的测试运行器。这里的builder为默认的AllDefaultPossibilitiesBuilder类型。之后我们将builder作为入参调用computer的getSuite()方法,在这个方法中,我们将会根据不同的条件逐步创建出我们需要的Runner。具体内容如下:

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);
    }

    /**
     * Create a single-class runner for {@code testClass}, using {@code builder}
     */
    protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
        return builder.runnerForClass(testClass);
    }

这里的代码逻辑就比较清楚了,我们返回了一个Suite对象,这个对象可以根据传入的参数Buidler对象,调用Budiler对象的runnerForClass()方法,获取相应的Runner,由于我们这里使用的Builder为AllDefaultPossibilitiesBuilder类型的对象,所以也就自然调用了这个类型中的runnerForClass()方法:

public Runner runnerForClass(Class<?> testClass) throws Throwable {
        List<RunnerBuilder> builders= Arrays.asList(
                ignoredBuilder(),
                annotatedBuilder(),
                suiteMethodBuilder(),
                junit3Builder(),
                junit4Builder());

        for (RunnerBuilder each : builders) {
            Runner runner= each.safeRunnerForClass(testClass);
            if (runner != null)
                return runner;
        }
        return null;
    }
protected JUnit4Builder junit4Builder() {
        return new JUnit4Builder();
    }

    protected JUnit3Builder junit3Builder() {
        return new JUnit3Builder();
    }

    protected AnnotatedBuilder annotatedBuilder() {
        return new AnnotatedBuilder(this);
    }

    protected IgnoredBuilder ignoredBuilder() {
        return new IgnoredBuilder();
    }

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

在这里我们获取了所有可用的RunnerBuilder对象,然后使用这些对象来创建合适的Runner,当我们的测试类中没有任何的类注解时,那么默认就会返回JUnit4Builder锁创建的Runner:

public Runner runnerForClass(Class<?> testClass) throws Throwable {
        return new BlockJUnit4ClassRunner(testClass);
}

也就是我们在第一篇博客中提到的默认Runner:BlockJUnit4ClassRunner。
然后获取Runner的过程就已经结束,我们回到Request中的classes()方法中,我们看到,当获取到合适的Runner后,我们将Runner作为入参调用了runner()方法,然后将Runner封装在了一哥Request中,并返回Request对象:

public static Request runner(final Runner runner) {
        return new Request(){
            @Override
            public Runner getRunner() {
                return runner;
            }       
        };
    }

这样,就获取到了Request,然后返回到了最开始的JunitCore类中的run方法中,然后继续调用重载的run()方法:

public Result run(Runner runner) {
        Result result= new Result();
        RunListener listener= result.createListener();
        fNotifier.addFirstListener(listener);
        try {
            fNotifier.fireTestRunStarted(runner.getDescription());
            runner.run(fNotifier);
            fNotifier.fireTestRunFinished(result);
        } finally {
            removeListener(listener);
        }
        return result;
    }

在这里,我们使用了观察者模式,注册了监听器,然后调用Runner的run()方法,开始进行测试。
在前面我们知道这里使用的runner为默认的BlockJUnit4ClassRunner类型,在这个类中找不到run()方法,原因是BlockJUnit4ClassRunner继承了ParentRunner类,而run()方法就在ParentRunner中:

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

在这里我们首先获取了代表要运行的测试封装对象Statement,在这里Statement表示一个或多个要被执行的测试方法,其中只有一个方法,用来执行测试。我们看一下Statement对象的获取过程:

//方法1
protected Statement classBlock(final RunNotifier notifier) {
        Statement statement= childrenInvoker(notifier);
        statement= withBeforeClasses(statement);
        statement= withAfterClasses(statement);
        statement= withClassRules(statement);
        return statement;
    }
//方法2
protected Statement childrenInvoker(final RunNotifier notifier) {
        return new Statement() {
            @Override
            public void evaluate() {
                runChildren(notifier);
            }
        };
    }
//方法3
private void runChildren(final RunNotifier notifier) {
        for (final T each : getFilteredChildren())
            fScheduler.schedule(new Runnable() {            
                public void run() {
                    ParentRunner.this.runChild(each, notifier);
                }
            });
        fScheduler.finished();
    }
//方法4
private List<T> getFilteredChildren() {
        if (fFilteredChildren == null)
            fFilteredChildren = new ArrayList<T>(getChildren());
        return fFilteredChildren;
    }

我们这里主要说明上面四个方法,首先在classBlock()方法中我们通过执行childrenInvoker()重写了Statement对象的执行方法evaluate(),那么重写的内容是什么呢,我们需要看runChildren()的实现,在这个方法中我们首先通过getFilteredChildren()获取了一个List,那么这个List中的内容是什么呢?在这里又调用了getChildren(),这个方法是个模板方法,由子类实现,还记得我们是怎么进入ParentRunner的吗,是的,是通过BlockJUnit4ClassRunner,那么这个方法的实现就是在BlockJUnit4ClassRunner中:

@Override
    protected List<FrameworkMethod> getChildren() {
        return computeTestMethods();
    }
protected List<FrameworkMethod> computeTestMethods() {
        return getTestClass().getAnnotatedMethods(Test.class);
    }

在这个方法中返回了List类型,代表被@Test注解的需要执行的测试方法,这样我们就找到了需要被执行的方法,就可以开始测试了。我们再回到ParentRunner的runChildren(),在我们获取到测试方法后,我们使用fScheduler对象依次调用runChild()方法,同样,这也是一个模板方法,方法实现还是在BlockJUnit4ClassRunner中:

@Override
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
        Description description= describeChild(method);
        if (method.getAnnotation(Ignore.class) != null) {
            notifier.fireTestIgnored(description);
        } else {
            runLeaf(methodBlock(method), description, notifier);
        }
    }

在这里主要的代码是runLeaf(methodBlock(method), description, notifier);这一句,在这里methodBlock()负责构造Statement对象,runLeaf()负责具体执行:

protected Statement methodBlock(FrameworkMethod method) {
        Object test;
        try {
            test= new ReflectiveCallable() {
                @Override
                protected Object runReflectiveCall() throws Throwable {
                    return createTest();
                }
            }.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);
        return statement;
    }
    /**
     * Returns a {@link Statement} that invokes {@code method} on {@code test}
     */
    protected Statement methodInvoker(FrameworkMethod method, Object test) {
        return new InvokeMethod(method, test);
    }

public class InvokeMethod extends Statement {
    private final FrameworkMethod fTestMethod;
    private Object fTarget;

    public InvokeMethod(FrameworkMethod testMethod, Object target) {
        fTestMethod= testMethod;
        fTarget= target;
    }

    @Override
    public void evaluate() throws Throwable {
        fTestMethod.invokeExplosively(fTarget);
    }
}

public Object invokeExplosively(final Object target, final Object... params)
            throws Throwable {
        return new ReflectiveCallable() {
            @Override
            protected Object runReflectiveCall() throws Throwable {
                return fMethod.invoke(target, params);
            }
        }.run();
    }

我们可以看到这里有一个InvokeMethod类继承自Statement类,其中封装了要执行的测试方法以及监听。其中的evaluate()方法调用了测试方法的invokeExplosively(),然后测试方法便得到了执行。我们再一次回到BlockJUnit4ClassRunner的methodBlock(),在这里,我们获取到Statement对象之后,又分别获取了被@After,@Before,@Rule,@Test()中的expected,timeout注解的各个方法以及属性,并进行封装,这个过程形成了一个链状结构,虽然返回的对象都是Statement,但是方法中其实返回的是Statement的子类:ExpectException,FailOnTimeout,RunBefores,RunAfters,RunRules。
具体内容请大家自行参考源码。然后我们看一下runLeaf()方法:

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();
        }
    }

这里的代码就很简单了,就仅仅调用了我们之前说过的被重载后的statement.evaluate();方法,这样测试方法就可以得到了执行。现在我们再一次回到ParentRunner的classBlock()方法中,到这里我们已经获取了一个接近完整的Statement对象,然后在classBlock()方法中,我们继续对注解有@BeforeClass,@AfterClass以及@ClassRule的部分进行封装,过程与之前的@After注解封装大体一致,这样子Statement就已经封装完了所有的需要执行的注解,然后将Statement对象返回,在ParentRunner中最终得到运行。
好了,今天就分享到这里,希望能够对大家有所帮助,如有不足之处,还请多多指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值