最近在研究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. Laravel -- 模型

    模型文件 <?php namespace App; use Illuminate\Database\Eloquent\Model; class Student extends Model { / ...

  2. 移植Linux Kernel SM750 驱动到VxWorks 7

    一.SM750简介 SM750 是SiliconMotion 推出的一款适合嵌入式设备的显卡(Embedded GPU),采用PCIe接口与CPU连接,内部集成16MB DDR SDRAM显存,产品具 ...

  3. Java学习笔记十一:Java中的方法

    Java中的方法 一:什么是方法: 所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块. 学过C语言或者其他语言的应该都知道函数这个东西,在Java中,其实方法就是函数,只不过叫法不同,在 ...

  4. Intellij 出现“Usage of API documented as @since 1.4+”的解决办法

    https://blog.csdn.net/wust_lh/article/details/73277185

  5. mysql 5.8 查询最新一条数据

    SELECT * FROM ( ,) FROM (SELECT * FROM or_task_node ORDER BY created_date DESC) temp ) AS vars ) t g ...

  6. HDU暑假多校第四场J-Let Sudoku Rotate

    一.题意 Sudoku is a logic-based, combinatorial number-placement puzzle, which is popular around the wor ...

  7. 在编程的时候,NotePad++ 中闪烁的光标突然有竖着闪烁的编程蓝色下划线闪烁的--小技巧告诉你-费元星

    当在写代码时出现的光标闪烁(横线闪烁) 在键盘上找 Insert ,按这个Insert就可以把横向闪烁光标( _ )修改成竖向闪烁光标样式( | ),横向光标会在你写代码的时候修改前面的代码,把光标移 ...

  8. 在Linux上进行mySql安装部署及遇到的问题的解决方法

    前提: Linux centOS虚拟机64位 1.首先确认是否已安装过MySQL 方法一:删除原有的MySQL目录: 使用查找语句: whereis mysql find / -name mysql ...

  9. SQL 注入教程

    SQL 注入测评教程 1     准备 安装包:Burpsuit.Python27.sqlmap 2     安装配置 2.1    Burpsuit 1)       解压Burpsuit 2)   ...

  10. Java并发基础--多线程基础

    一.多线程基础知识 1.进程和线程 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程:进程也是程序的一次执行过程,是系统运行程序的基本单位:系统运行 ...