使用Spock进行基于属性的测试

基于属性的测试测试的替代方法,是对基于示例的测试的补充。 后者是我们一生都在做的事情:针对“示例”行使生产代码-我们认为这些输入具有代表性。 挑选这些示例本身就是一门艺术:“普通”输入,边缘情况,格式错误的输入等。但是为什么我们将自己限制为仅几个示例呢? 为什么不测试成千上万个… 所有输入? 这种方法至少有两个困难:

  1. 规模。 一个仅接受一个int输入的纯函数将需要40亿次测试。 这意味着数百GB的测试源代码和几个月的执行时间。 如果一个函数需要两个int ,则将其平方。 对于String来说,实际上达到了无穷大。
  2. 假设我们有在量子计算机或类似设备上执行的这些测试。 您如何知道每个特定输入的预期结果? 您可以手动输入(祝您好运),也可以生成预期的输出。 通过生成,我的意思是编写一个程序,为每个输入生成期望值。 但是我们不是首先就已经测试了这样的程序吗? 我们是否要编写更好的,无错误版本的被测代码只是为了对其进行测试? 也称为丑陋的镜子反图案

因此,您了解测试每一个输入(尽管比较理想)只是一项心理实验,无法实现。 话虽这么说,基于属性的测试试图尽可能地接近该测试的必杀技。 问题#1通过用数百或数千个随机输入猛击被测代码来解决。 不是全部,甚至不是一小部分。 但是是一个很好的随机表示。

问题2出奇地困难。 基于属性的测试可以生成随机参数,但是无法计算出该随机输入的预期结果。 因此,我们需要一种不同的机制来命名整个哲学。 我们必须提出无论输入是什么,被测代码都表现出的属性(不变性,行为)。 从理论上讲,这听起来很合理,但是在各种情况下都有许多这样的属性:

  1. 任何数字的 绝对值 都不应为负
  2. 编码和解码任何字符串都应为每种对称编码返回相同的String
  3. 对于任何输入 ,某些旧算法的优化版本应产生与旧算法相同的结果
  4. 在以任意顺序进行任意数量的银行间交易之后,银行的总金额应保持不变

如您所见,我们可以想到许多属性,其中没有提到特定的示例输入。 这不是详尽而严格的测试。 这更像是采样,并确保样本“理智”。 有很多库支持几乎每种语言的基于属性的测试。 在本文中,我们稍后将探讨Spock和ScalaCheck。

Spock +自定义数据生成器

Spock不支持现成的基于属性的测试。 但是,在数据驱动测试和第三方数据生成器的帮助下,我们可以走得很远。 Spock中的数据表可以归纳为所谓的数据管道

def 'absolute value of #value should not be negative'() {
    expect:
    value.abs() >= 0

    where:
    value << randomInts(100)
}

private static def List<Integer> randomInts(int count) {
    final Random random = new Random()
    (1..count).collect { random.nextInt() }
}

上面的代码将生成100个随机整数,并确保所有.abs()均为非负数。 您可能会认为该测试相当愚蠢,但是令人惊讶的是它实际上发现了一个错误! 但是首先让我们杀死一些样板代码。 产生随机输入,尤其是更复杂的输入,既麻烦又无聊。 我发现了两个可以帮助我们的库。 鸡胚的发生

import spock.genesis.Gen

def 'absolute value of #value should not be negative'() {
    expect:
    value.abs() >= 0

    where:
    value << Gen.int.take(100)
}

看起来不错,但是如果要生成例如随机整数列表, net.java.quickcheck具有更好的API,并且不是特定于Groovy的:

import static net.java.quickcheck.generator.CombinedGeneratorsIterables.someLists
import static net.java.quickcheck.generator.PrimitiveGenerators.integers

def 'sum of non-negative numbers from #list should not be negative'() {
    expect:
    list.findAll{it >= 0}.sum() >= 0

    where:
    list << someLists(integers(), 100)
}

这个测试很有趣。 通过生成100个随机int ,确保非负数之和永远不会为负。 听起来很合理。 但是,多次测试均失败。 首先,由于整数溢出有时两个正int ■添加到一个负值。 ! 实际上,发现的另一种失败类型令人恐惧。 虽然[1,2,3].sum()为6,但是[].sum()为… nullWAT?

如您所见,即使是最简单,最基本的基于属性的测试也可以在数据中发现异常情况。 但是等等,我说测试int绝对值发现了一个错误。 实际上,由于数据生成器差(“随机性”太差),它并不是没有首先返回已知的边沿值。 我们将在下一篇文章中解决该问题。

翻译自: https://www.javacodegeeks.com/2014/09/property-based-testing-with-spock.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值