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


    接上一篇【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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    上一篇说到
    runner是 org.junit.internal.builders.AllDefaultPossibilitiesBuilder#runnerForClass得到的返回对象:

        public Runner runnerForClass(Class testClass) throws Throwable {
            List builders = Arrays.asList(
                    ignoredBuilder(),
                    annotatedBuilder(),
                    suiteMethodBuilder(),
                    junit3Builder(),
                    junit4Builder());
    
            for (RunnerBuilder each : builders) {
                Runner runner = each.safeRunnerForClass(testClass);
                if (runner != null) {
                    return runner;
                }
            }
            return null;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    **我们看看JUnit4Builder **

    public class JUnit4Builder extends RunnerBuilder {
        @Override
        public Runner runnerForClass(Class testClass) throws Throwable {
            return new BlockJUnit4ClassRunner(testClass);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    看代码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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    @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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    @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();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    @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
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

        @Override
        protected List getChildren() {
            return computeTestMethods();
        }
    
    • 1
    • 2
    • 3
    • 4

    调用方法:

        protected List computeTestMethods() {
            return getTestClass().getAnnotatedMethods(Test.class);
        }
    
    • 1
    • 2
    • 3

    清晰了:是把所有标记 @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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

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

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

    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();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    @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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

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

    over~~

  • 相关阅读:
    SSRF漏洞JAVA解决方案
    Qt的信号与槽
    如何有效防止网络攻击?老男孩教育给你支招!
    thinkPhp5返回某些指定字段
    redis key 命名规范 键名称中的冒号 : 命名空间层次的表示
    高额补贴!武汉市科技型企业保证保险贷款补贴申报条件、流程和材料
    Linux中怎么启动Zookeeper
    【Pygame 学习笔记】8.精灵
    【Linux网络(一)初识计算机网络】
    栈题目:解析布尔表达式
  • 原文地址:https://blog.csdn.net/Aqu415/article/details/128055450