首页 > 代码库 > 【JUnit4.10源码分析】5.2 Rule

【JUnit4.10源码分析】5.2 Rule


标注@Rule

TestRule是一个工厂方法模式中的Creator角色——声明工厂方法。

package org.junit.rules;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public interface TestRule {
	Statement apply(Statement base, Description description);
}

因为工厂方法apply有參数base。因而TestRule将创建装饰模式中的装饰对象

【抽象类Statement声明操作evaluate()的接口。它作为一个回调接口,上层模块能够定义各种Statement的子类。提供evaluate()的方法体。而这一主要的技术与Rule结合,成为JUnit一个很重要的手段——能够说它是一个通用型的复合命令的构造方式。全然能够代替Statement的一些复合命令的子类如ExpectException等。

測试程序猿要使用Rule,必须编写代码。以下先介绍使用Rule的样例。

1.MyStatement——Statement的一个新的装饰对象。( JUnit之Rule的使用中的样例),地位等同于ExpectException。

package rule;
import static tool.Print.*;//pln(Object)
import org.junit.runners.model.Statement;
/**
 *
 * @author yqj2065
 */
public class MyStatement extends Statement {
    private final Statement base;
    public MyStatement( Statement base ) {
        this.base = base;
    }

    @Override public void evaluate() throws Throwable {
        pln( "before...sth..sth" );
        try {
            base.evaluate();
        } finally {
             pln( "after...sth..sth" );
        }
    }
}
2.一个详细的工厂。也就是定义工厂方法的TestRule的子类。MyRule将创建一个Statement的新的装饰对象MyStatement。如同工厂方法模式的一般使用方法,详细工厂与详细产品一一相应。

package rule;
import org.junit.rules.TestRule;
import org.junit.runners.model.Statement;
import org.junit.runner.Description;
public class MyRule implements TestRule  {
    @Override
    public Statement apply(Statement base, Description description) {
        return new MyStatement( base );
    }
}
3.使用新的装饰对象

用户类BlockJUnit4ClassRunner使用的是ExpectException等JUnit内建的Statement装饰对象;測试程序猿编写的Statement装饰对象,则须要通过@Rule,嵌入到单元測试类中

package rule;
import org.junit.Rule;
import org.junit.Test;
public class MyTest {   
    @Rule
    public MyRule myRule = new MyRule();
    
    @Test()
    public void xx() {
        System.out.println( "xx()..." );
    }
}
在开发环境中执行MyTest,输出:
before...sth..sth
xx()...

after...sth..sth

例程 8 17标注Rule
package org.junit;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Rule {}

注意:JUnit4.8中Rule标注并没有指定@Target(ElementType.FIELD),而4.10版本号中明白定义出来。Rule用于修饰測试单元类中一个特殊的域。该域必须是public、非static并且是org.junit.rules.TestRule子类型的引用变量(代替4.8版本号的org.junit.rules.MethodRule)。JUnit4.10版本号中还有一个标注@ClassRule,用于修饰測试单元类中static的TestRule子类型的引用变量。


TestRule

org.junit.rules.TestRule声明工厂方法,因为工厂方法apply有參数base,因而TestRule将创建装饰模式中的装饰对象。

然而,装饰对象仍然是一个Statement。

TestRule的各种实现类创建很多装饰对象。可是没有特别的名字。反正是一个Statement。

从JUnit框架设计的角度看,TestRule是工厂方法;而从測试程序猿思考问题的角度,TestRule及事实上现类,为单元測试制订了各种附加的规则/Rule。

在org.junit.rules包中的TestRule的实现类有:

1. TestWatcher

对base加以监控的Rule。它不会对结果进行改动。

TestWatcher以匿名类的方式创建Statement装饰对象。在@Override evaluate()时使用了模板方法模式

例程 8-4 TestWatchman
package org.junit.rules;

import org.junit.internal.AssumptionViolatedException;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public abstract class TestWatcher implements TestRule {
	public Statement apply(final Statement base, final Description description) {
		return new Statement() {
			@Override
			public void evaluate() throws Throwable {
				starting(description);
				try {
					base.evaluate();
					succeeded(description);
				} catch (AssumptionViolatedException e) {
					throw e;
				} catch (Throwable t) {
					failed(t, description);
					throw t;
				} finally {
					finished(description);
				}
			}
		};
	}
	protected void succeeded(Description description) {	}
	protected void failed(Throwable e, Description description) {	}
	protected void starting(Description description) {	} 
	protected void finished(Description description) {	}
}

TestWatcher的子类TestName是模板方法模式中的详细类角色。

       publicvoid starting(FrameworkMethod method) {

              fName=method.getName();

       }

2. Verifier

使用了模板方法模式,以匿名类的方式创建Statement装饰对象,在base. evaluate()之后加入一个verify()操作。Verifier的子类ErrorCollector收集測试中的错误。

3. ExpectedException和Timeout

ExpectedException作为工厂创建装饰对象,其私有内部类ExpectedExceptionStatement全然等价于org.junit.internal.runners.statements.ExpectException的地位。可是,ExpectedException作为规则将作用于各种測试方法。而ExpectException作用于@Test(expected=xxx)修饰的方法

Timeout则是典型的工厂。返回一个装饰对象FailOnTimeout

package org.junit.rules;
import org.junit.internal.runners.statements.FailOnTimeout;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class Timeout implements TestRule {
	private final int fMillis;

	/**
	 * @param millis the millisecond timeout
	 */
	public Timeout(int millis) {
		fMillis= millis;
	}

	public Statement apply(Statement base, Description description) {
		return new FailOnTimeout(base, fMillis);
	}
}

4. ExternalResource

ExternalResource使用了模板方法模式。以匿名类的方式创建Statement装饰对象。在base. evaluate()之前before()中能够加入各种其它资源(file, socket, server, database connection),而after()中释放资源。其子类TemporaryFolder给出了一个样例。




【JUnit4.10源码分析】5.2 Rule