如何保证FsCheck的重现性

时间:2014-03-03 11:14:16

标签: unit-testing f# nunit fscheck

我们希望在持续集成中使用FsCheck作为单元测试的一部分。因为这种确定性和可重复的行为对我们来说非常重要。

作为随机测试框架的FsCheck可以生成有时可能会破坏的测试用例。关键是,我们不仅要使用必须包含每个输入的属性,比如说List.rev >> List.rev === id。但是,我们做了一些数字测试,一些测试用例可能导致测试因为条件恶劣而中断。

问题是:我们如何保证,一旦测试成功,它将永远成功?

到目前为止,我看到以下选项:

  • 硬编码种子,例如这将是最简单的解决方案。
  • 制作非常具体的自定义生成器,以避免不好的例子。当然可能,但结果可能很难,特别是如果有很多对象要生成。
  • 与它一起生活,在某些情况下,由于病态情况,构建可能是红色的,只需重新运行。

在这样的设置中使用FsCheck的惯用方法是什么?

2 个答案:

答案 0 :(得分:4)

  

某些测试案例可能导致测试因严重条件而中断。

听起来你需要条件属性:

let isOk x =
    match x with
    | 42 -> false
    | _ -> true

let MyProperty (x:int) = isOk x ==> // check x here...

(假设你不喜欢42号。)

答案 1 :(得分:3)

(我开始撰写评论,但是我觉得它应该得到自己的答案。)

使用FsCheck测试不适用于每个输入的属性是很常见的。例如,如果您为List.rev运行,则FsCheck会轻易驳斥您的list<float>示例。

数值稳定性本身就是一个棘手的问题 - FsCheck中没有任何非确定性可归咎于此(FsCheck完全是确定性的,它只是一个输入生成器......)。您所指的“非确定性”可能是某些处理器中的浮点运算中的错误等等。但即使在那种情况下,你不想知道他们吗?如果算法在数值上对于一类输入不稳定,你不想知道它吗?如果你不这样做,在我看来,就像你正在为一些真正的非决定论做准备......在生产中。

在FsCheck中写入不适用于给定类型的所有输入的属性的惯用方法是编写生成器和放大器。收缩机。您可以使用==>作为其中的一步,但它不能很好地扩展到复杂的前提条件。你说这可能会变得非常困难 - 这是正确的,因为我保证你会了解你的代码。好事!

修复种子是一个坏主意,除了重现以前发现的错误。我的意思是,在实践中你会做什么:继续重新运行测试直到它通过,然后修复种子并宣布“完成工作”?