5

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

FsCheck 是一个随机测试框架,可以生成有时可能会中断的测试用例。关键是,我们不仅使用必须为每个输入保留的属性,例如 say List.rev >> List.rev === id。而是,我们做一些数字,一些测试用例可能会因为条件不好而导致测试中断。

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

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

  • 对种子进行硬编码,例如 0。这将是最简单的解决方案。
  • 制作非常具体的自定义生成器,以避免不良示例。当然可能,但结果可能非常困难,尤其是在要生成许多对象的情况下。
  • 忍受它,在某些情况下,由于病理情况,构建可能是红色的,并且只是重新运行。

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

4

2 回答 2

4

一些测试用例可能会因为条件不好而导致测试中断。

这听起来像你需要一个条件属性:

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

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

(假设你不喜欢数字 42。)

于 2014-03-03T11:55:29.447 回答
2

(我开始写评论,但它太长了,我想它应该得到自己的答案)。

使用 FsCheck 测试并不适用于每个输入的属性是很常见的。例如,List.rev如果您为list<float>.

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

在 FsCheck 中编写不适用于给定类型的所有输入的属性的惯用方法是编写生成器和收缩器。您可以使用==>它作为一个步骤,但它不能很好地扩展到复杂的先决条件。你说这可能会变得非常困难——这是真的,因为我保证你会学到一些关于你的代码的东西。一件好事!

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

于 2014-03-04T08:21:26.440 回答