自动化测试工具 junit4 执行 cases 背后的故事 (2)---默认执行器 blockjunit4classrunner 源码分析

胡刚 · 2015年10月07日 · 最后由 胡刚 回复于 2015年10月08日 · 2043 次阅读

(1)背景:

之前《JUnit4 执行 cases 背后的故事 (1)---JUnitCore 源码分析》介绍了 JUnit4 执行测试用例默认的执行器是 BlockJUnit4ClassRunner,它内部是怎么实现的呢? 本文将一一介绍。

(2)org.junit.runners.BlockJUnit4ClassRunner 是什么?

官方解释如下:

it is now the default test class runner, but it should have exactly the same behavior as the old test class runner (JUnit4ClassRunner).

BlockJUnit4ClassRunner has advantages for writers of custom JUnit runners that are slight changes to the default behavior, however:

It has a much simpler implementation based on Statements, allowing new operations to be inserted into the appropriate point in the execution flow.
It is published, and extension and reuse are encouraged, whereas JUnit4ClassRunner was in an internal package, and is now deprecated.

概括来说:

BlockJUnit4ClassRunner 是默认的测试类执行器,可以通过它方便地实现自定义执行器(可以在执行流中插入新的操作),它执行测试的方式是基于 Statement block,Statement 分成数种职责(BeforeClass, AfterClass,Before,After,执行测试方法等),不同的 Statement 负责自己的职责,执行结束后交给下一个 Statement,直到所有 Statement 执行完毕;设计模式是职责链模式(Chain Of Responsibility Pattern)。

类继承关系:

java.lang.Object
  extended by org.junit.runner.Runner  
      extended by org.junit.runners.ParentRunner<FrameworkMethod>
          extended by org.junit.runners.BlockJUnit4ClassRunner

(3)源码分析

执行用例时,会执行runner.run(this.notifier);(见JUnit4 执行 cases 背后的故事 (1)---JUnitCore 源码分析 第三部分),该 runner 为 BlockJUnit4ClassRunner 实例,调用其父类 ParentRunner 方法:

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

run() 方法调用 classBlock(notifier) 新建 Statement 实例(类 Statement block)。

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

classBlocke() 方法调用 childrenInvoker() 新建 Statement 实例(方法 Statement block)

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

childrenInvoker() 新建 Statement 实例,该实例重写 evaluate() 方法,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();
        }
    }

getFilteredCildren() 返回List<FrameworkMethod>, 每个 FrameworkMethod 实例是@Test标记的测试方法。
getFilteredCildren() 方法内部通过 getChildren(),computeTestMethods() 获取到 List。

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


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

runChildren() 遍历List<FrameworkMethod>, 对每个 FrameworkMethod 调用 BlockJUnit4ClassRunner 的 runChild() 作为 RunnerScheduler currentScheduler 实例 (RunnerScheduler:Represents a strategy for scheduling when individual test methods should be run (in serial or parallel)) 的执行体:

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

如果该方法未被标记@Ignore,则获取到该方法的 Statement:

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

methodInvoker() 方法会新建 InvokeMethod 实例 (Statement 子类),InvokeMethod 类重写 evaluate() 方法。

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

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

evaluate() 执行体 testMethod.invokeExplosively(target), target 值为 test,invokeExplosively() 方法返回@Test测试方法的执行结果。

/**
     * Returns the result of invoking this method on {@code target} with
     * parameters {@code params}. {@link InvocationTargetException}s thrown are
     * unwrapped, and their causes rethrown.
     */
    public Object invokeExplosively(final Object target, final Object... params)
            throws Throwable {
        return new ReflectiveCallable() {
            @Override
            protected Object runReflectiveCall() throws Throwable {
                return method.invoke(target, params);
            }
        }.run();
    }

runChild() 调用父类 ParentRunner 的 runLeaf() 方法(方法 Statement block 部分)并执行该 Statement block(通过 statement.evaluate());

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

通过 Statement statement = childrenInvoker(notifier) 获取 method Statem block 后,再获取 class Statement block。

statement = withBeforeClasses(statement);
    statement = withAfterClasses(statement);
    statement = withClassRules(statement);

最后调用:statement.evaluate();依次执行 evaluate()...

@Override
    public void run(final RunNotifier notifier) {
        EachTestNotifier testNotifier = new EachTestNotifier(notifier,
                getDescription());
        try {
            Statement statement = classBlock(notifier);
            statement.evaluate();

(4)总结

1.如果想在 class Statement block 加入自定义动作,可以修改 classBlock() 方法。
2.如果想在 method Statement block 加入自定义动作,可以修改 methodBlock() 方法。
3.如果想在@Test方法加入自定义动作,可以修改 methodInvoker() 方法。

所有修改必须定义一个 Statement 子类,并重写 evaluate() 方法定义自己的职责。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 2 条回复 时间 点赞

语法用 java 高亮吧

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册