假设要评选JUnit中最最重要的类型。或者说核心,无疑是org.junit.runners.model.Statement。Runner等类型看起来热闹而已。

package org.junit.runners.model;
/**
* Represents one or more actions to be taken at runtime in the course
* of running a JUnit test suite.
*/
public abstract class Statement {
/**
* Run the action, throwing a {@code Throwable} if anything goes wrong.
*/
public abstract void evaluate() throws Throwable;
}

Statement是命令模式中的Command、装饰模式中的Component。它是Rule发挥作用的关键。

写完JUnit4.8.2源码分析-5.1 Statement之复合命令之后,yqj2065就对JUnit的源码的研读缺乏激情鸟。JUnit4.10的源码在此BlockJUnit4ClassRunner没有明显的变化。

读完最精致的设计。其它还有什么好玩的呢?可能就是并行。可是并行是一种Java技术,和反射、注解、泛型一样的技术问题。不是精致的设计。准备花点时间好好写点Statement的东西补充本文。先挖坑。JUnit4.8.2源码分析-5Statement JUnit之Rule的使用先凑活着看。

命令模式的Command角色

抽象类Statement声明操作evaluate()的接口,而由其子类——详细命令或复合命令负责绑定命令的Recelver/接收者或命令的运行者。

Statement/语句是对执行JUnit測试组过程中的一个或多个动作的封装。假设说Runner.run()表示执行JUnit測试组的整个过程,则Statement表示当中或大或小的步骤。针对方法的标注如@Test 、@Before、@After、@BeforeClass、@AfterClass和各种測试场景。Statement的子类封装一个或多个动作。

最基础的详细命令有Fail和InvokeMethod。

Fail表示JUnit測试过程中遇到了问题而失败,因而其evaluate()简单地抛出一个异常。源码例如以下:

package org.junit.internal.runners.statements;
import org.junit.runners.model.Statement;
public class Fail extends Statement {
private final Throwable fError;
public Fail(Throwable e) { fError= e; } @Override public void evaluate() throws Throwable {
throw fError;
}
}

InvokeMethod表示执行一个@Test方法的命令,这是JUnit測试的最小单元。

package org.junit.internal.runners.statements;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement; public class InvokeMethod extends Statement {
private final FrameworkMethod fTestMethod;
private Object fTarget;
public InvokeMethod(FrameworkMethod testMethod, Object target) {
fTestMethod= testMethod;
fTarget= target;
} @Override
public void evaluate() throws Throwable {
fTestMethod.invokeExplosively(fTarget);
}
}

InvokeMethod提供构造器注入的方式。获得測试单元类的对象target和FrameworkMethod。命令的运行者为FrameworkMethod对象,參数是由外界注入的Object
target。

这两个样例。说明了命令模式中选择命令的运行者的极大自由。

其它的子类,属于复合命令

BlockJUnit4ClassRunner实际上是命令模式中的Client。直接调用详细的命令。依照JUnit4.8.2源码分析-5Statement所言。将BlockJUnit4ClassRunner相关代码放在MethodBlock中后,BlockJUnit4ClassRunner中仅仅须要把
methodBlock(method).evaluate();

改动为 new MethodBlock(method,test).evaluate();

抽象类Statement声明操作evaluate()的接口,它作为一个回调接口,上层模块能够定义各种Statement的子类。提供evaluate()的方法体。

而这一主要的技术与Rule结合。成为JUnit一个很重要的手段——能够说它是一个通用型的复合命令的构造方式。全然能够代替Statement的一些复合命令的子类如ExpectException等。

复合命令子类如ExpectException由JUnit框架的设计者提供,而使用Rule,则将复合命令子类的构造任务交给測试程序猿。所以,一般的常常使用的复合命令,还是应该作为默认实如今框架中存在

装饰模式中的Component角色

复合命令是对基本命令的拓展装饰模式的目的就是给一个对象增添一些其它的功能。简单而典型的ExpectException和FailOnTimeout,针对@Test注解中的參数。比如@Test(expected=IndexOutOfBoundsException.class)或@Test(timeout=100)。以ExpectException为例(源码中的Ststement
fNext,我改成了base

package org.junit.internal.runners.statements;
import org.junit.internal.AssumptionViolatedException;//意料之中的异常
import org.junit.runners.model.Statement; public class ExpectException extends Statement {
private Statement base;
private final Class<? extends Throwable> fExpected; public ExpectException(Statement base, Class<? extends Throwable> expected) {
this.base= base;
fExpected= expected;
} @Override
public void evaluate() throws Exception {
boolean complete = false;
try {
base.evaluate();
complete = true;
} catch (AssumptionViolatedException e) {
throw e;
} catch (Throwable e) {
if (!fExpected.isAssignableFrom(e.getClass())) {
String message= "Unexpected exception, expected<"
+ fExpected.getName() + "> but was<"
+ e.getClass().getName() + ">";
throw new Exception(message, e);
}
}
if (complete)
throw new AssertionError("Expected exception: "
+ fExpected.getName());
}
}

ExpectException依赖注入Statement对象——通常为@Test注解的方法的Statement。和预期的异常expected。

ExpectException在base的基础上增添了对预期的异常的处理。

base.evaluate(),假设抛出了意料之中的异常,转手抛出。假设不是意料之中的异常,说明这是一个"Unexpected exception,……";假设顺利运行没有异常。则抛出断言错误——我预期的异常在哪里?

在BlockJUnit4ClassRunner中使用代码对@Test(expected=xxx)进行的处理:

package org.junit.runners;
public class MethodBlock extends Statement{//从BlockJUnit4ClassRunner中提取出来
@Deprecated
protected Statement possiblyExpectingExceptions(FrameworkMethod method,
Object test, Statement next) {
Test annotation= method.getAnnotation(Test.class);
return expectsException(annotation) ? new ExpectException(next,
getExpectedException(annotation)) : next;
}
private Class<? extends Throwable> getExpectedException(Test annotation) {
if (annotation == null || annotation.expected() == None.class)
return null;
else
return annotation.expected();
} private boolean expectsException(Test annotation) {
return getExpectedException(annotation) != null;
}
}

因为使用了反射机制,装饰对象的创建与常见的方式Person b = new T2(newT2(new T1(newLawyer())))不同,须要代码处理:

new ExpectException(next, getExpectedException(annotation)

注意。假设@Test没有指定(expected=xxx),possiblyExpectingExceptions()返回一个主要的Statement。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXFqMjA2NQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

因为使用了反射机制,装饰模式中用户类BlockJUnit4ClassRunner须要某个Builder(在JUnit中不过一个方法)创建装饰对象ExpectException。

而这就是TestRule的作用,一个工厂方法模式中的Creator角色。

【JUnit4.10源码分析】5 Statement的更多相关文章

  1. 【JUnit4.10源码分析】6.1 排序和过滤

    abstract class ParentRunner<T> extends Runner implements Filterable,Sortable 本节介绍排序和过滤. (尽管JUn ...

  2. JUnit4.12 源码分析之Statement

    1. Statement 抽象类Statement作为命令模式的Command,只有一个方法 各种Runner作为命令模式中的Invoker,将发出各种Statement,来表示它们运行JUnit测试 ...

  3. 【JUnit4.10源码分析】5.2 Rule

    标注@Rule TestRule是一个工厂方法模式中的Creator角色--声明工厂方法. package org.junit.rules; import org.junit.runner.Descr ...

  4. 【JUnit4.10源码分析】3.4 Description与測试树

    Description使用组合模式描写叙述一个測试树.组合模式中全部元素都是Composite对象. Description有成员变量private final ArrayList<Descri ...

  5. JUnit4.12 源码分析之TestClass

    1. TestClass // 源码:org.junit.runners.model.TestClass // 该方法主要提供方法校验和注解搜索 public class TestClass impl ...

  6. 10.源码分析---SOFARPC内置链路追踪SOFATRACER是怎么做的?

    SOFARPC源码解析系列: 1. 源码分析---SOFARPC可扩展的机制SPI 2. 源码分析---SOFARPC客户端服务引用 3. 源码分析---SOFARPC客户端服务调用 4. 源码分析- ...

  7. MyBatis 源码分析——生成Statement接口实例

    JDBC的知识对于JAVA开发人员来讲在简单不过的知识了.PreparedStatement的作用更是胸有成竹.我们最常见用到有俩个方法:executeQuery方法和executeUpdate方法. ...

  8. JUnit4.12 源码分析(二)之TestRule

    1. TestRule TestRule和@Before,@After,@BeforeClass,@AfterClass功能类似,但是更加强大; JUnit 识别TestRule的两种方式: 方法级别 ...

  9. 11.源码分析---SOFARPC数据透传是实现的?

    先把栗子放上,让大家方便测试用: Service端 public static void main(String[] args) { ServerConfig serverConfig = new S ...

随机推荐

  1. Your build settings specify a provisioning profile with the UUID “65a1f48d-2fa3-44eb-8142-627d9d4455b1”, however, no such provisioning profile was found.

    Your build settings specify a provisioning profile with the UUID “65a1f48d-2fa3-44eb-8142-627d9d4455 ...

  2. php内核一 一次请求与结束

    php开始 到 结束 有两个阶段 请求开始之间的初始化阶段 请求之后的结束处理阶段 开始阶段: 模块初始化 模块激活 模块初始化:    在整个SAPI生命周期内,只执行一次(apache服务器启动的 ...

  3. [转]使用ping钥匙临时开启SSH:22端口,实现远程安全SSH登录管理就这么简单

    原文链接:使用ping钥匙临时开启SSH:22端口,实现远程安全SSH登录管理就这么简单 这个留待后面玩一下,还是有安全隐患,非核心业务 临时用一下可以. 设置防火墙策略时,关于SSH:22访问权限, ...

  4. 有意思的C宏

    在Linux内核.嵌入式代码等传统的C代码里,会有一些难以识别的宏定义.我记得在eCos, UBoot, FFmpeg有一些比较BT的宏定义,很难读懂.对于C++程序员来说,最好将这种难读的宏定义转成 ...

  5. dump文件定位程序崩溃代码行

    1.dump文件 2.程序对应的pdb 步骤一:安装windbg 步骤二:通过windbg打开crash dump文件 步骤三:设置pdb文件路径,即符号表路径 步骤四:运行命令!analyze -v ...

  6. Warning: Unable to send packet: Error with PF_PACKET send() [11]: Message too long (errno = 90)

    今天在使用tcpreplay重放流量时,发现有的数据包没有发送成功: Warning: Unable to send packet: Error with PF_PACKET send() [215] ...

  7. Jquery构建Form表单Post提交数据的简单方法

    $.extend({ PostSubmitForm: function (url, args) { var body = $(document.body), form = $("<fo ...

  8. Java创建线程的细节分析

    转载:http://shmilyaw-hotmail-com.iteye.com/blog/1880902 前言 关于线程创建的问题,可以说是老生常谈了.在刚开始学习Thread的时候基本上都会接触到 ...

  9. 五毛的cocos2d-x学习笔记01-创建项目

    终于准备开始学习cocos2d-x了.因为想和同学一起做游戏参加比赛,所以打算学习很热的Cocos2d-x.因为已经学习了C++,所以我想入门应该不是很困难.再加上官网有中文教程以及多不胜数的游戏开发 ...

  10. vim 多文件编辑【超实用】

    vim 多文件编辑(偶的linux笔记) http://blog.csdn.net/lcj_cjfykx/article/details/18805721 通过vim打开的每个文件都对应着一个buff ...