Junit4 架构设计系列(2): Runner.run()与Statement

Overall##

系列入口:

Junit4 架构设计系列(1): Request,ClassRequest 和 RunnerBuilder

前文中,我们基本理清了Junit4执行Case大体上的Flow:
Request -> ClassRequest.getRunner() -> AllDefaultPossibilitiesBuilder.safeRunnerForClass() -> runner.run()

并且介绍了类Request,ClassRequest,和RunnerBuilder,剩下runner.run()没讲,那本文就从这说起。

Runner.run()是如何执行Case的?##

run()方法是抽象类Runner定义的一个方法,目的就是执行Case。各个Runner的子类都要实现这个方法。

 /**
 * Run the tests for this runner.
 *
 * @param notifier will be notified of events while tests are being run--tests being
 * started, finishing, and failing
 */
public abstract void run(RunNotifier notifier);

从前文知道,默认负责执行Junit4风格case的Runner是BlockJUnit4ClassRunner, 但是BlockJUnit4ClassRunner并不是直接继承与Runner类,而是中间多了一层ParentRunner<T>, 如下图:

ParentRunner类负责filter 和 sort Test Class, 处理 @BeforeClass and @AfterClass 方法, 和各种 ClassRules, 并且按顺序执行Test Class。

那我们来看看ParentRunner是如何执行Junit4风格的Test Class的,具体实现如下:

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

第一行是实例化一个EachTestNotifier,主要为了记录执行过程的,这里暂且不做详细解释。
很明显,Try里的逻辑才是真正的执行步骤。逻辑也很清晰,就是得到Statement,然后调用evaluate()方法。

继续跟进方法classBlock,我们就会看到下面的逻辑:

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

这个逻辑是用于组合我们期望的Statement,不难看出,Runner.run()方法主要涉及下面几个执行逻辑:

  • childInvoker方法表示要执行执行这条Test Class了
  • withBeforeClasses则是判断,Test Class有没有被@BeforeClass修饰的方法?要是有的话就要先执行它
  • withAfterClasses则判断Test Class有没有@AfterClass修饰的方法
  • withClassRules这个则是检测Test Class有没有适用TestRules

当所有的statement包装好后,调用statement.evaluate()就可以按要求,按顺序的执行我们希望的结果了。

看到这种嵌套输入与输出的写法,会不会有中恍然大悟的感觉?!这不就是装饰者模式的经典适用场景嘛。

要注意的是,这里处理的都是Class,而对于Method级,实际上也有类似装饰者模式的适用场景, 我们从上面childInvoker跟进去,最终会发现,真正执行Test Method的是BlockJunit4ClassRunner类,首先它实现了ParentRunner的抽象方法runChild:

//
// Implementation of ParentRunner
//

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

然后在期方法块中methodBlock组合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;
}

跟ClassBlock异曲同工。

详解Statement##

Statement在Junit4中是非常重要的一块,完全可以大书特书,如图:

在JUnit4中,一切动作都可以用Statement来表示,从上图包的分配方式我们可以看到,Junit4不但预定义了一些必须使用或者使用频率高的动作,如

  • InvokeMethod 执行Test Method
  • RunBefores 先于InvokeMethod之前执行@Before方法
  • RunAfters 执行@After方法
  • Fail Throw Errors
  • FailOnTime 设置Timeout的入口
  • ExpectException 定义期望所抛出的异常

还定义了RunRules,能够让我们Reuse或者是重新定义自己的Rule.可谓方便至极.

设计模式知识补充##

Decorator模式###

Decorator(装饰者)模式动态地将责任添加到对象上。在扩展功能方面,装饰者模式提供了比继承更有弹性的替代方案。
这是我实现的标准装饰者模式的类图:

而装饰者模式特点就是:

  • 装饰者和被装饰者对象有相同的超类型
  • 我们可以用多个装饰者去包装一个对象
  • 装饰者可以在所委托被装饰者的行为之前/或之后,就上自己的行为,以达到特定的目的
  • 对象可以在运行时动态地,不限量的用装饰者来装饰

高亮的第三点很关键,我们知道Junit4之所以能让 @BeforeClass 和 @AfterClass 等注解,按要求或前或后的执行,就是利用了这一点。

敬请期待##

后续计划

  • Junit4架构设计系列(3) RunNotifer

童鞋,如果觉得本文还算用心,还算有用,何不点个赞呢(⊙o⊙)?

posted @ 2015-08-06 22:35  大卡尔  阅读(1696)  评论(1编辑  收藏  举报