【JUnit4.10源代码分析】6 Runner

org.junit.runner.Runner是JUnit的工作引擎。它在诸多类型的支持下,处理测试并产生(Description)、Failure和Result等输出。

Runner的主要类层次如图所示。

【JUnit4.10源代码分析】6 Runner_第1张图片

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>

排序和过滤暂时不讨论。

<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。

这些抽象方法是ParentRunner<T>最重要的内容。Must be overridden


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)来设置。

classBlock

正如【JUnit4.10源代码分析】5.2 Rule所介绍的,TestRule声明工厂方法,TestRule的实现类将创建装饰模式中的Statement装饰对象。classBlock(final RunNotifier notifier)代码也创建一个复杂的Statement装饰对象,按照yqj2065的想法,应该将相关代码提取出来。然而,childrenInvoker(notifier)和withClassRules(statement)代码的抽取并不容易。

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

1.Statement statement= childrenInvoker(notifier);

给出了ParentRunner的基本操作,我们将private void runChildren(final RunNotifier notifier)内联后,代码为:

    protected Statement childrenInvoker(final RunNotifier notifier) {
        return new Statement() {
            @Override public void evaluate() {
                for (final T each : getFilteredChildren())
                    fScheduler.schedule(new Runnable() {			
                            public void run() {
                                ParentRunner.this.runChild(each, notifier);
                            }
                        });
                fScheduler.finished();
            }
        };
    }
它的确以 匿名类的方式创建Statement装饰对象,然而它与ParentRunner耦合太强,代码中调用了 (第4行)getFilteredChildren()、(第5行)依赖域fScheduler、(第6行)调用了待子类提供实现的runChild()。因而可以将它设计成内部类而非一个方法,看起来比较舒服一点。
    protected class ChildrenInvoker extends Statement{
        final RunNotifier notifier;
        ChildrenInvoker(RunNotifier notifier){
            this.notifier =notifier;
        }

        @Override public void evaluate() {
            for (final T each : getFilteredChildren())
                fScheduler.schedule(new Runnable() {			
                        public void run() {
                            ParentRunner.this.runChild(each, notifier);
                        }
                    });
            fScheduler.finished();
        }
    }
statement= withBeforeClasses(statement);
statement= withAfterClasses(statement);

这两部分的代码很容易放在一个新类中,如同JUnit4.8.2源代码分析-5.1 Statement之复合命令演示的那样。
        statement= withClassRules(statement);

放在一个新类中,需要提供fTestClass和getDescription()两个参数。

如此这般的折腾,就是体会一下装饰模式在使用中可以出现的各种变体。如 TestRule这种工厂方法(规则)、或直接的方法形式如JUnit目前的实现




你可能感兴趣的:(框架,源代码,JUnit4.10)