Junit执行源码分析,Junit是怎么跑起来的(二)

这篇博客深入解析了JUnit的执行源码,从JUnitCore的run方法开始,逐步剖析了如何构建并选择合适的Runner,如JUnit3Builder和JUnit4Builder。重点在于BlockJUnit4ClassRunner如何执行测试类及其内部方法,包括@BeforeClass、@AfterClass、@Test等注解的处理。整个过程涉及策略模式和责任链模式,展示了面向对象设计的运用。
摘要由CSDN通过智能技术生成

接上一篇【Junit执行源码分析,junit是怎么跑起来的】 https://blog.csdn.net/Aqu415/article/details/127494898 这里我们接着分析

org.junit.runner.JUnitCore#run(org.junit.runner.Runner)

这个方法

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

@A:执行runer的run方法,会执行我们的待测试目标方法

上一篇说到
runner是 org.junit.internal.builders.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;
    }

junit默认提供了几种Runner,比如听过的Junit3和熟悉的Junit4(其他的没听过 哈哈);
这里会决定返回哪一种 Runner;
从设计模式上来说这里运用的策略模式:每一种runner会根据目标测试类判解析是否需要自己来处理,如果是由自己来处理则返回一个Runner类;就好像说:嗯,你是我的菜,并把菜装在篮子里返回

举个例,我们看看 JUnit3Builder 是怎么判的该类是自己的菜的

public class JUnit3Builder extends RunnerBuilder {
    @Override
    public Runner runnerForClass(Class<?> testClass) throws Throwable {
        if (isPre4Test(testClass)) {
            return new JUnit38ClassRunner(testClass);
        }
        return null;
    }

    boolean isPre4Test(Class<?> testClass) {
        return junit.framework.TestCase.class.isAssignableFrom(testClass);
    }
}

如果类上标注了 @TestCase注解,那么这个类就会由JUnit3执行

**我们看看JUnit4Builder **

public class JUnit4Builder extends RunnerBuilder {
    @Override
    public Runner runnerForClass(Class<?> testClass) throws Throwable {
        return new BlockJUnit4ClassRunner(testClass);
    }
}

看代码JUnit4是一个兜底的方案,其他Runner不吃的菜全部由他来吃;嗯 了不起

BlockJUnit4ClassRunner

上面我们看到了JUnit4Builder 返回了一个BlockJUnit4ClassRunner对象;

在这里插入图片描述
BlockJUnit4ClassRunner继承了 ParentRunner,ParentRunner的run方法就是执行测试类方法的入口(上面标记@A的地方)。

org.junit.runners.ParentRunner#run
    @Override
    public void run(final RunNotifier notifier) {
        EachTestNotifier testNotifier = new EachTestNotifier(notifier,
                getDescription());
        try {
            // @A
            Statement statement = classBlock(notifier);
            // @B
            statement.evaluate();
        } catch (AssumptionViolatedException e) {
            testNotifier.addFailedAssumption(e);
        } catch (StoppedByUserException e) {
            throw e;
        } catch (Throwable e) {
            testNotifier.addFailure(e);
        }
    }

@A:方法源码如下

org.junit.runners.ParentRunner#classBlock
    protected Statement classBlock(final RunNotifier notifier) {
        // @A1
        Statement statement = childrenInvoker(notifier);
        // @A2
        if (!areAllChildrenIgnored()) {
            statement = withBeforeClasses(statement);
            statement = withAfterClasses(statement);
            statement = withClassRules(statement);
        }
        return statement;
    }

@A1:找到标记了 @Test注解的方法
@A1:把测试类里标注了 @BeforeClass(静态方法) 、@AfterClass(静态方法)等注解的方法找到并包装成 Statement对象,设计模式应该属于责任链模式,形成一个链式数据结构

@B 最终调用的方法是 org.junit.runners.ParentRunner#runChildren

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

@C:最终调用的是ParentRunner子类BlockJUnit4ClassRunner的runChild方法

org.junit.runners.BlockJUnit4ClassRunner#runChild

跟了一圈代码最终方法执行到
org.junit.runners.BlockJUnit4ClassRunner#runChild

    @Override
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {   //@C0
        Description description = describeChild(method);
        // @C1
        if (isIgnored(method)) {
            notifier.fireTestIgnored(description);
        } else {
         // @C3
            runLeaf(methodBlock(method), description, notifier); // @C2
        }
    }

@C0: method参数是通过org.junit.runners.ParentRunner#getFilteredChildren方法获得的,通过一连串分析,得知其是子类 org.junit.runners.BlockJUnit4ClassRunner#getChildren返回值

    @Override
    protected List<FrameworkMethod> getChildren() {
        return computeTestMethods();
    }

调用方法:

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

清晰了:是把所有标记 @Test注解的方法获取到

@C1:被忽略的方法,不被执行

@C2:methodBlock方法源码分析:

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

这里跟上面ParentRunner行为差不多,会把目标类里标注了 @Before、@After的方法和目标方法串联起来,这里区别的是:ParentRunner是把标注了@BeforeClass、和@AfterClass的静态方法串联起来。

这里会返回一个Statement 对象,源码如下

    protected Statement methodInvoker(FrameworkMethod method, Object test) {
        return new InvokeMethod(method, test);
    }

Statement 对象将方法和其对应的类关联起来,返回的是一个InvokeMethod对象;

@C3:runLeaf方法

protected final void runLeaf(Statement statement, Description description,
            RunNotifier notifier) {
        EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
        eachNotifier.fireTestStarted();
        try {
            // @D
            statement.evaluate();
        } catch (AssumptionViolatedException e) {
            eachNotifier.addFailedAssumption(e);
        } catch (Throwable e) {
            eachNotifier.addFailure(e);
        } finally {
            eachNotifier.fireTestFinished();
        }
    }

@D:即执行上面Statement 的evaluate方法,最终即执行 InvokeMethod 类的evaluate方法;

public class InvokeMethod extends Statement {
    private final FrameworkMethod testMethod;
    private final Object target;

    public InvokeMethod(FrameworkMethod testMethod, Object target) {
        this.testMethod = testMethod;
        this.target = target;
    }

    @Override
    public void evaluate() throws Throwable {
        testMethod.invokeExplosively(target);
    }
}

这样就执行到我们的目标方法了。

不得不说初次来看代码很绕,不花点时间折腾调试还真摸不着头脑;但如果你能站在更高的角度,去思考完成这个功能所需的设计,也许就不觉得那么饶了;
核心我觉得还是面向对象的一些思想:封装、继承、多态;代码复用、设计模式等等

over~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值