zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    人机猜拳
    M1分数分配
    postmortem report of period M1
    视频文档分享网站场景测试说明书
    功能规格说明书
    11.9Daily Scrum
    11.6Daily Scrum
    需求博客
    11.5Daily Scrum
    11.3Daily Scrum
  • 原文地址:https://www.cnblogs.com/wavky/p/JUnit4_Theory.html
Copyright © 2011-2022 走看看