你读过数学理论吗?


它看起来通常像这样:


对于所有的a,b>0,以下是正确的:a+b>a,a+b>b。


只是我们看到的定义通常难以理解。


譬如可以这样描述:它囊括了一个相当大的范围内(在此是无穷大)的所有元素(或者是元素的组合)。


与此相对应,一个典型的测试片段如下:


@Test

 public void a_plus_b_is_greater_than_a_and_greater_than_b(){

   int a = 2;

   int b = 3;

   assertTrue(a + b > a);

   assertTrue(a + b > b);

 }


这仅仅是对我们所谈论的大集合中的一个元素所进行的定义。不是很让人印象深刻。当然我们可以通过在测试上进行循环(或者使用参数化测试)来稍微休整一下这个问题。


@Test

public void a_plus_b_is_greater_than_a_and_greater_than_b_multiple_values() {

   List<Integer> values = Arrays.asList(1, 2, 300, 400000);

   for (Integer a : values)

     for (Integer b : values) {

        assertTrue(a + b > a);

        assertTrue(a + b > b);

     }

   }


当然这仍然只测试了几个值,而且代码也看起来更难看了。我们竟然使用了9行代码来测试只写了一行的数学理论。而且最关键的是,在转化中应该对任意a,b值都适用的约束关系也完全消失了。


JUnit Theories带来了希望。让我们看一下使用这种强大的工具写出来的测试是什么样子的。


import org.junit.experimental.theories.DataPoints;

import org.junit.experimental.theories.Theories;

import org.junit.experimental.theories.Theory;

import org.junit.runner.RunWith;

 

import static org.junit.Assert.assertTrue;

 

@RunWith(Theories.class)

public class AdditionWithTheoriesTest {

 

  @DataPoints

  public static int[] positiveIntegers() {

       return new int[]{

                        1, 10, 1234567};

  }

 

  @Theory

  public void a_plus_b_is_greater_than_a_and_greater_than_b(Integer a, Integer b) {

      assertTrue(a + b > a);

      assertTrue(a + b > b);

  }

}


使用JUnit Theories工具,测试被分成了两个部分:一个是提供数据点集(比如待测试的数据)的方法,另一个是理论本身。这个理论看起来几乎就像一个测试,但是它有一个不同的注解(@Theory),并且它需要参数。类通过使用数据点集的任意一种可能的组合来执行所有理论。


这意味着,如果我们有和测试主题相符的一个以上的理论,我们只需要声明一次数据点集。因此,让我们添加下面的理论,对加法来说应该是是正确的:a+b=b+a。所以我们将下面的理论添加至我们的类。


@Theory

public void addition_is_commutative(Integer a, Integer b) {

    assertTrue(a + b == b + a);

}


这看起来很有魅力,你已经开始看到我们因为没有重复声明相同的数据点集,而少写了一部分代码。但我们仅仅对正整数进行了测试,而交换性是适用于所有整数的!当然我们的第一条理论仍然只对正数有效。


对此问题同样有相应的解决方案,那就是:Assume类。使用assume使得你可以在对理论测试前首先检查一下前提条件。如果条件不是一个正确的给定参数集,那么此理论将会跳过此参数集。所以我们的测试现在看起来像这样:


@RunWith(Theories.class)

 public class AdditionWithTheoriesTest {

 

  @DataPoints

  public static int[] integers() {

     return new int[]{

                   -1, -10, -1234567,1, 10, 1234567};

  }

 

  @Theory

  public void a_plus_b_is_greater_than_a_and_greater_than_b(Integer a, Integer b) {

     Assume.assumeTrue(a >0 && b > 0 );

     assertTrue(a + b > a);

     assertTrue(a + b > b);

  }

 

  @Theory

  public void addition_is_commutative(Integer a, Integer b) {

     assertTrue(a + b == b + a);

  }

}


这使得测试进行了很好的表述。


除了简洁,由测试/理论模型实现的对测试数据进行的分离还有另外一点好处:你可能会开始考虑使你的测试数据独立于实际的东西来测试。


让我们开始这样做。如果你想要测试一个接受一个整数参数的方法,什么样的整数可能会造成问题呢?下面是我的建议:


@DataPoints

  public static int[] integers() {

     return new int[]{0, -1, -10, -1234567,1, 10, 1234567, Integer.MAX_VALUE, Integer.MIN_VALUE};}


这样测试我们的例子当然会失败了。如果你让Integer.MAX_VALUE加上一个正整数,将会得到一个溢出的值!所以我们了解到用当前形式所描述的理论是错误的!是的,这显而易见,但请再看看当前的项目。确实需要用MIN_VALUE,MAX_VALUE,0,正数和负数来进行所有使用整数的测试吗?是啊,确实应该如此。


那么更复杂的项目呢?字符串、日期、集合或者是域对象?使用JUnit Theories,你只需建立一次测试数据生成器,以用来创建所有更易产生问题的场景,然后在所有使用理论的测试中进行重用。这将会使你的测试更具表述力,也提高了发现错误的概率。


更多相关文章

  1. 测试领域,小白问题大集合(适合未入门和入门初级者)
  2. Swagger 自动生成 Dubbo 服务的接口文档,以及测试调用
  3. Linux性能优化(八)——网络测试工具
  4. 用 cURL 请求测试 ETag 浏览器缓存[每日前端夜话0xCC]
  5. Linux性能优化(十三)——CPU性能测试
  6. 如何在angularjs代码中单元测试jquery元素
  7. js去除html标记的测试问题。很多分!
  8. 这是一个关于HTML编辑器的功能测试
  9. android json php测试输出垃圾

随机推荐

  1. 【转】Android kernel启动流程
  2. Libcurl库移植指南(下)--编译支持https的
  3. Android P WiFi自动连接评分机制
  4. 【30篇突击 android】源码统计 十六
  5. Android 去掉运营商STK对话框提示
  6. Android中自定义ProgressBar
  7. 逆向工具/反编译工具 集合
  8. Android 权限被拒绝,跳转至权限设置界面
  9. AndroidManifest中activity属性设置大全
  10. android:HttpClient请求(get、post)