org.junit.runner.Runner是JUnit的工作引擎。它在诸多类型的支持下,处理测试并产生(Description)、Failure和Result等输出。
Runner的主要类层次如图所示。
Describable与Runner
为了保证Runner的子类都有一个Description(虚域模式)数据来源/成员变量,Runner implements Describable。
package org.junit.runner; public interface Describable { /** * @return a {@link Description} showing the tests to be run by the receiver */ public abstract Description getDescription(); }
按照注释可知,runner.getDescription()将返回消息接收者runner将运行的测试的Description/测试树。JUnit中Runner是一个抽象类,
package org.junit.runner; import org.junit.runner.notification.RunNotifier; public abstract class Runner implements Describable { //public abstract Description getDescription();//可以省略 public abstract void run(RunNotifier notifier); public int testCount() { return getDescription().testCount(); } }
yqj2065不喜欢其中的便宜方法testCount(),或者说我更希望将Runner设计成接口。
ParentRunner<T>
排序和过滤暂时不讨论。
ParentRunner<T>的取名,意味着本运行器是测试树的某个"parent node" 的运行器。然而对运行器而言,"parent node"只有单元测试类和成组测试类,因而ParentRunner<T>有两个子类型BlockJUnit4ClassRunner和Suite。其他非父结点的Runner,有IgnoredClassRunner、ErrorReportingRunner等。
ParentRunner<T>的类型参数T,代表其某种孩子的类型。这是一个较别致的设计。
private List<T> fFilteredChildren= null;
protected abstract List<T> getChildren();
protected abstract Description describeChild(T child);
protected abstract void runChild(T child, RunNotifier notifier);
BlockJUnit4ClassRunner针对一个单元测试类,它没有子运行器;因而此时T为FrameworkMethod。
Suite针对一个成组测试类,它有若干子运行器,T为Runner。
由此可知,运行器对应测试树也是一个树形结构,而一个单元测试类仅拥有一个BlockJUnit4ClassRunner。
RunnerScheduler
该接口表示运行测试方法时的时序调度策略,是顺序执行还是并行。在ParentRunner<T>中默认采用顺序执行,于是我们看见了难得一见的Runnable.run()的直接调用。
private RunnerScheduler fScheduler= new RunnerScheduler() { public void schedule(Runnable childStatement) { childStatement.run(); } public void finished() { // do nothing } };
也可以通过public void setScheduler(RunnerScheduler scheduler)来设置。