最近在研究JUnit4,大部分基础技术都是通过百度和JUnit的官方wiki学习的,目前最新的发布版本是4.11,结合代码实践,发现官方wiki的内容或多或少没有更新,Theory理论机制章节情况尤为严重,不知道这章wiki对应的是第几版,笔主在4.11版本中是完全跑不通的,因为接口结构已经改变了,而百度出来的博客文档更是只有Theory的基础部分,更具实际应用价值的扩展部分完全不见踪影,本文根据笔主实际编码总结经验,详细讲述如何使用4.11版JUnit的Theory理论机制。

ps. 最近发现网上有一小撮别有用心的国人转载笔主文章时,顺手丢羊把笔主文章头部标注的原文地址恶意马赛克掉,也没有在文后贴出转载地址,为了普及技术笔主就不追究了,这次故意在贴在这里,本文地址:JUnit4.11 理论机制 @Theory 完整解读

笔主下面所使用代码,仅依赖于 junit-4.11.jar,建议同时导入 hamcrest-core-1.3.jar、hamcrest-library-1.3.jar 以提供完整的assertThat支持

简单介绍Theory

使用注解@Theory取代@Test标记测试方法,可以支持带形参的测试方法签名,并使用指定的数据集自动代入进行连续多次测试,虽然暂未看到官方或其他个人的文字表述,但笔主觉得这个机制是参数化测试 Parameterized tests 的优化扩展版(Parameterized tests需要独占一个测试类,兼容性灵活性都太差了)。

使用@Theory测试方法需要在测试类头部声明@RunWith(Theories.class)

传统Theory

Theory的基础部分,通过显式预定义各种Class类型变量,在@Theory的带参测试方法中自动逐次传入相同Class类型的变量值,运行多次测试(依据预定义变量数量而定),复数形参情况使用预定义变量排列组合出实参对进行代入测试。

1、使用@DataPoint标记待用实参数据

 @DataPoint
public static String **1 = “####”;
@DataPoint
public static String **2 = “####”;
@DataPoint
public static String **3 = “####”;

2.1、使用@Theory标记单形参测试方法

 @Theory
// 仅输入与形参相同类型(String)的预定义@DataPoint数据,此处会自动轮流代入上面的 **1 - **3 作为实参测试数据,运行3次test1测试
public void test1(String ***) {
// 使用assume设置过滤
assumeThat(***);
assertThat(***);
}

2.2、复数形参测试方法

 @Theory
// 使用排列组合与形参同类型(String)@DataPoint自动化输入,与命名、顺序无关,此处s1、s2将轮流使用 **1 - **3 的排列组合,如s1=**1,s2=**2或s1=**2,s2=**1,运行次数依据排列组合方案数量而定
public void test2(String s1, String s2) {
assumeThat(***);
assertThat(***);
}

Theory扩展(Popper project)

这是本文的核心部分,传统Theory指定实参变量的方式存在非常明显的局限性,无法精确控制每个形参的可用变量值范围,因此JUnit引入Popper 项目的技术,提供一种完全自定义指定实参数据集的方法。

定制@Theory测试方法实参变量值范围,主要使用 Parameter Supplier 结构

系统自带默认实现

JUnit中自带一个默认的 Parameter Supplier 实现:@TestedOn(ints = int[])

使用方式示例:

 @Theory
public final void test(@TestedOn(ints = { 0, 1, 2 }) int i) {
assertTrue(i >= 0);
}

在这个例子中,可以很直观的看到形参 i 在实际运行测试中,会依次自动代入取值为 ints 指定的数组{0, 1, 2}中每个元素

完全自定义实现

JUnit默认只提供了一个int型的简单 Parameter Supplier 实现,而Theory机制真正的价值在于,能参考@TestedOn的做法,相对简单的完全自定义出可重用 Parameter Supplier,适应于各种复杂要求的限定范围参数值测试场景,满足开发者所需的高度动态自定义范围取值自动化测试,同时保留与一般@Test相同的强大兼容性,作为本文核心中的战斗机,下面将通过两个栗子,详细描述如何一步步实现自定义 Parameter Supplier。

自定义实现I(动态实参值表)

本方式以一个自定义注解接口@Between为例,展示如何通过读取注解属性变量值,动态创建相应的实参数据集。

1、定义annotation注解接口 Between:

 @Retention(RetentionPolicy.RUNTIME)
// 声明注解接口所使用的委托处理类
@ParametersSuppliedBy(BetweenSupplier.class)
public @interface Between{
// 声明所有可用参数,效果为 @Between([first = int,] last = int)
int first() default 0; // 声明默认值,成为非必须提供项
int last();
}

2、定义委托处理类 BetweenSupplier:

 public class BetweenSupplier extends ParameterSupplier{

     @Override
public List<PotentialAssignment> getValueSources(ParameterSignature sig) { // 自定义实参值列表
List<PotentialAssignment> list = new ArrayList<PotentialAssignment>(); // 获取注解变量
Between between = sig.getAnnotation(Between.class); // 获取通过注解@Between传入的first值
int first = between.first(); // 获取通过注解@Between传入的last值
int last = between.last(); for (int i = first; i <= last; i++){
   // PotentialAssignment.forValue(String name, Object value)
   // name为value的描述标记,没实际作用
   // value为实参可选值
list.add(PotentialAssignment.forValue("name", i));
}
return list;
}
}

3、调用方式:

 @Theory
public final void test(@Between(last = 0) int i, @Between(first = 3, last= 10) int j) {
// i 取值为 0(first默认=0,last=0),j 取值为 3-10
assertTrue(i + j >= 0);
}

自定义实现II(静态实参值表):

本方式以一个自定义注解接口@AllValue为例,展示如何静态地内部创建固定的实参数据集。

1、定义annotation注解接口 AllValue(按“使用II”方式调用可省略此步骤):

 @Retention(RetentionPolicy.RUNTIME)
// 声明注解接口所使用的委托处理类
@ParametersSuppliedBy(AllValueSupplier.class)
// 空接口,不需要接受任何参数变量
public @interface AllValue{ }

2、定义委托处理类 AllValueSupplier:

 public class AllValueSupplier extends ParameterSupplier{

     @Override
public List<PotentialAssignment> getValueSources(ParameterSignature sig) { List<PotentialAssignment> list = new ArrayList<PotentialAssignment>();
  
   // 内定提供固定的可用实参值表
for (int i = 0; i <= 100; i++){
list.add(PotentialAssignment.forValue("name", i));
}
return list;
}
}

3.1、使用I(使用自定义注解):

 @Theory
public final void test(@AllValue int i) {
// i 取值为 0-100
assertTrue(i >= 0);
}

3.2、使用II(可省略第1步注解接口AllValue的定义):

 @Theory
public final void test(@ParametersSuppliedBy(AllValueSupplier.class) int i) {
// i 取值为 0-100
assertTrue(i >= 0);
}

JUnit官方wiki及下载地址:https://github.com/junit-team/junit/wiki

Theory理论机制英文原文wiki(注:已过期,不适用于4.11):https://github.com/junit-team/junit/wiki/Theories

JUnit4.11 理论机制 @Theory 完整解读的更多相关文章

  1. 【Static Program Analysis - Chapter 4】格理论(Lattice Theory)与程序分析

    # 从一个例子说起, **任务:给定这样一段代码,假设我们想分析出这段代码中,每个数值型变量和表达式的符号,即正数,负数或0.** 此外,还有可能出现两种情况就是: 1.我们无法分析出结果,即我们无法 ...

  2. c++11并发机制

    传统意义上OS提供的并发机制包含进程和线程两个级别.考虑到实际复杂性,c++11仅提供了线程并发机制. c++11提供的线程并发机制主要位于四个头文件中:..... 线程并发机制包括线程管理.原子操作 ...

  3. [转]JUnit-4.11使用报java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing错误

    原文引自: http://blog.csdn.net/castle07/article/details/8553704 今天尝试使用JUnit,下载了最新的JUnit版本,是4.11,结果尝试使用发现 ...

  4. JUnit-4.11使用报java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing错误

    今天尝试使用JUnit,下载了最新的JUnit版本,是4.11,结果尝试使用发现总是报java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribi ...

  5. 2019微软Power BI 每月功能更新系列——Power BI 4月版本功能完整解读

    Power BI4月份的更新对整个产品进行了重大更新.此版本增加了基于DAX表达式定义视觉效果标题和按钮URL的功能.本月Power BI也新增了许多新的连接器,现在可以使用几种预览连接器,包括Pow ...

  6. 11.Spark Streaming源码解读之Driver中的ReceiverTracker架构设计以及具体实现彻底研究

    上篇文章详细解析了Receiver不断接收数据的过程,在Receiver接收数据的过程中会将数据的元信息发送给ReceiverTracker:   本文将详细解析ReceiverTracker的的架构 ...

  7. 深入浅出 Java Concurrency (11): 锁机制 part 6 CyclicBarrier

      如果说CountDownLatch是一次性的,那么CyclicBarrier正好可以循环使用.它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).所谓屏障 ...

  8. Maven-Eclipse使用maven创建HelloWorld Java项目,使用Junit-4.11的注解

    1.针对前面创建的mavenTest项目,我们做一些修改,包括pom.xml.App.java.AppTest.java 说明:其中的scope属性,如果是test,表示该依赖只对测试有效,如果不声明 ...

  9. 深入浅出 Java Concurrency (11): 锁机制 part 6 CyclicBarrier[转]

    如果说CountDownLatch是一次性的,那么CyclicBarrier正好可以循环使用.它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).所谓屏障点就 ...

随机推荐

  1. 用友二次开发之科脉TOT3凭证接口

    按客户的要求,根据科脉导出的数据,开发一个工具,将凭证导入T3 这个科目导出的凭证格式. 选择账套登陆,你没看错,这个是我开发的登陆界面. 选择接口文件. 软件自动进数据分类,你可以看到数据了.但只是 ...

  2. elasticsearch 5.x 系列之五 数据导入导出

    一.首先给大家发一个福利,分享一个elasticsearch 数据导出工具. esm github 源码地址: https://github.com/medcl/esm 下载编译好的对应elastic ...

  3. ruby Dir类

    类方法 1. Dir[pat]    Dir::glob( pat) 返回一个数组,包含与指定的通配符模式 pat 匹配的文件名: * - 匹配包含 null 字符串的任意字符串 ** - 递归地匹配 ...

  4. xampps 不能配置非安装目录虚拟主机解决方案

    今天将前几天安装好的xampps配置下,准备开始php开发之旅,在我信心满满的将工作目录定在非安装目录上(安装目录在:D:\Program Files\xampps\apache\htdocs  我将 ...

  5. MapRudecer

    MapReducer基本概念 Mapreduce是一个分布式运算程序的编程框架,是用户开发“基于hadoop的数据分析应用”的核心框架: Mapreduce核心功能是将用户编写的业务逻辑代码和自带默认 ...

  6. git使用ssh密钥(转)

    git使用https协议,每次pull, push都要输入密码,相当的烦.使用git协议,然后使用ssh密钥.这样可以省去每次都输密码. 大概需要三个步骤:一.本地生成密钥对:二.设置github上的 ...

  7. golang log

    自带log模块 写入文件 package main import ( "fmt" "log" "os" ) func main(){ log ...

  8. 第四篇 Python循环

    While 循环 For 循环

  9. C计算了一下

    #include <stdio.h> int main(){ int a,b,c,e; a=6 + 5 / 4 - 2; b=2 + 2 * (2 * 2 - 2) % 2 / 3; c= ...

  10. sphinx调用API参考(官方手册)

    API的参考实现是用PHP写成的,因为(我们相信)较之其他语言,Sphinx在PHP中应用最广泛.因此这份参考文档基于PHP API的参考,而且这节中的所有的代码样例都用PHP给出. 当然,其他所有A ...