使用 FsCheck 我在测试中得到不同的结果,一次 100% 通过,另一次出错

Using FsCheck I get different results on tests, once 100% passed and the other time error

我创建了一个生成器来生成具有相同长度的 int 列表并测试压缩和解压缩的 属性。 运行我的测试偶尔出错

Error: System.ArgumentException: list2 is 1 element shorter than list1

但由于我的发电机,它不应该发生。

我得到了三次测试 100% 通过,然后出现上面的错误。为什么? 看来我的发电机工作不正常。

let samelength (x, y) = 
    List.length x = List.length y

let arbMyGen2 = Arb.filter samelength Arb.from<int list * int list> 

type MyGenZ =
    static member genZip() = 
       {
        new Arbitrary<int list * int list>() with
            override x.Generator = arbMyGen2 |> Arb.toGen
            override x.Shrinker t = Seq.empty
    }

let _ = Arb.register<MyGenZ>()

let pro_zip (xs: int list, ys: int list) = 
   (xs, ys) = List.unzip(List.zip xs ys)
   |> Prop.collect (List.length xs = List.length ys)

do Check.Quick pro_zip

你写的代码对我有用。所以我不确定到底出了什么问题,但我可以给你一些有用的(希望如此!)提示。

首先尝试不使用注册机制,而是使用Prop.forAll,如下:

let pro_zip  = 
   Prop.forAll arbMyGen2 (fun (xs,ys) ->
        (xs, ys) = List.unzip(List.zip xs ys)
        |> Prop.collect (List.length xs))

do Check.Quick pro_zip

请注意,我还更改了您的 Prop.collect 调用以收集列表的长度,这提供了更有趣的输出。事实上,您的 属性 已经检查列表是否具有相同的长度(尽管是隐含的),因此如果它们不是,则测试将失败并出现反例。

Arb.filter 将现有的 Arbitrary(即生成器 过滤器)转换为新的 Arbitrary。换句话说,arbMyGen2 有一个收缩函数可以工作(即只有 returns 个较小的等长列表对),而在 genZip() 中你把收缩器扔掉了。直接写

就好了
type MyGenZ =
    static member genZip() = arbMyGen2

相反。