JUnit4.11 理论机制 @Theory 完整解读

最近在研究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标记待用实参数据

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

 

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

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

 

2.2、复数形参测试方法

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

 

Theory扩展(Popper project)

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

 

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

 

系统自带默认实现

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

使用方式示例:

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

 

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

 

完全自定义实现

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

 

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

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

 

1、定义annotation注解接口 Between:

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

  

2、定义委托处理类 BetweenSupplier:

 1 public class BetweenSupplier extends ParameterSupplier{
 2 
 3     @Override
 4     public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
 5 
 6         // 自定义实参值列表
 7         List<PotentialAssignment> list = new ArrayList<PotentialAssignment>();
 8 
 9         // 获取注解变量
10         Between between = sig.getAnnotation(Between.class);
11 
12         // 获取通过注解@Between传入的first值
13         int first = between.first();
14 
15         // 获取通过注解@Between传入的last值
16         int last = between.last();
17  
18         for (int i = first; i <= last; i++){
19           // PotentialAssignment.forValue(String name, Object value)
20           // name为value的描述标记,没实际作用
21           // value为实参可选值
22             list.add(PotentialAssignment.forValue("name", i));
23         }
24         return list;
25     }
26 }

 

 3、调用方式:

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

 

 

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

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

 

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

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

  

2、定义委托处理类 AllValueSupplier:

 1 public class AllValueSupplier extends ParameterSupplier{
 2 
 3     @Override
 4     public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
 5         
 6         List<PotentialAssignment> list = new ArrayList<PotentialAssignment>();
 7   
 8       // 内定提供固定的可用实参值表
 9         for (int i = 0; i <= 100; i++){
10             list.add(PotentialAssignment.forValue("name", i));
11         }
12         return list;
13     }
14 }

  

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

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

  

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

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

 

 

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

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

posted @ 2014-01-17 09:53  wavky  阅读(3767)  评论(2编辑  收藏  举报