我们希望在持续集成中使用 FsCheck 作为单元测试的一部分。因此,确定性和可重复的行为对我们来说非常重要。
FsCheck 是一个随机测试框架,可以生成有时可能会中断的测试用例。关键是,我们不仅使用必须为每个输入保留的属性,例如 say List.rev >> List.rev === id。相反,我们做一些数字,一些测试用例可能会因为条件不好而导致测试中断。
问题是:我们如何保证,一旦测试成功,它就永远成功?
到目前为止,我看到以下选项:
在这种设置中使用 FsCheck 的惯用方法是什么?
在实现基于属性的测试时,何时应该在前置条件表达式上使用输入生成器?
选择特定选项时是否存在性能因素?
在内部,一种方法是否不可避免地使用另一种方法?
我认为与输入生成器相比,前置条件表达式需要更长的时间才能执行.有没有人测试过这个?
为什么我们需要两者?
我最初尝试创建一个固定前5个元素的生成器(并且在使用Prop.forAll前五个元素的任何测试中总是会运行),但是这样做失败了.
现在我试图通过为一个范围内的随机数据生成一个生成器,并为非随机数据生成一个生成器(即固定序列)来简化此操作.它类似于Gen.constant,除了代替一个值,它是一系列值.
我有这个(简化的可重现的例子,适用于NUnit和xUnit):
[<Property(Verbose = true, MaxTest=5)>]
static member MultiplyIdentityCornerCases () =
    Gen.elements [0L; -1L; 1L; Int64.MinValue; Int64.MaxValue]
    |> Arb.fromGen 
    |> Prop.forAll <| fun x -> x = x * 1L
输出是(不知道null它来自哪里):
0:
<null>
9223372036854775807L
1:
<null>
-9223372036854775807L
2:
<null>
-9223372036854775807L
3:
<null>
1L
4:
<null>
-9223372036854775807L
Ok, passed 5 tests.
我希望输出包含序列中的所有五个测试,最好是但不一定按顺序排列.我知道我可以使用测试数据提供程序使用NUnit(或任何单元测试系统),但我想知道我是否可以使用FsCheck(或者我是否应该这样做,也许这是一个坏主意).
我认为使用FsCheck是有用的,对于有多个函数参数的情况,我希望它能够详尽地测试我提供的极端情况参数的所有组合.使用FsCheck比使用testdata提供程序更容易.
我正在努力学习如何正确使用FsCheck,并将其与Expecto集成.如果我使用默认的FsCheck配置,我可以运行属性测试,但是当我尝试使用自己的Generator时,它会导致堆栈溢出异常.
这是我的发电机
type NameGen() =
    static member Name() =
        Arb.generate<string * string>
        |> Gen.where (fun (firstName, lastName) ->
            firstName.Length > 0 && lastName.Length > 0
        )
        |> Gen.map (fun (first, last) -> sprintf "%s %s" first last)
        |> Arb.fromGen
        |> Arb.convert string id
而我正试图像这样使用它:
let config = { FsCheckConfig.defaultConfig with arbitrary = [typeof<NameGen>] }
let propertyTests input =
    let output = toInitials input
    output.EndsWith(".")
testPropertyWithConfig config "Must end with period" propertyTests
在它进入Gen.where函数之前抛出异常
我究竟做错了什么?谢谢
FsCheck具有一些简洁的默认Arbitrary类型来生成测试数据。但是,如果我的一个考试日期取决于另一个考试日期怎么办?
例如,考虑一下string.Substring()结果子字符串的属性永远不能长于输入字符串:
[Fact]
public void SubstringIsNeverLongerThanInputString()
{
    Prop.ForAll(
        Arb.Default.NonEmptyString(),
        Arb.Default.PositiveInt(),
        (input, length) => input.Get.Substring(0, length.Get).Length <= input.Get.Length
    ).QuickCheckThrowOnFailure();
}
尽管Substring当然实现是正确的,但此属性会失败,因为最终PositiveInt会生成比生成的结果更长的a,NonEmptyString从而导致异常。
收缩:NonEmptyString“ a” PositiveInt 2,但例外:System.ArgumentOutOfRangeException:索引和长度必须引用字符串中的位置。
我可以if (input.Length < length) return true;用来保护比较,但是那样的话,即使没有检查该属性,我也会进行大量测试。
如何告诉FsCheck仅生成PositiveInt不超过输入字符串的?我想我必须使用Gen<T>该类,但是它的接口让我感到困惑……我尝试了以下操作,但仍然PositiveInt超出了字符串:
var inputs = Arb.Default.NonEmptyString();
// I have no idea what I'm doing here...
var lengths = inputs.Generator.Select(s => s.Get.Length).ToArbitrary();
Prop.ForAll(
    inputs,
    lengths,
    (input, length) => input.Get.Substring(0, length).Length <= input.Get.Length
).QuickCheckThrowOnFailure();
我有以下代码:
var gen = from x in Arb.Generate<int>()
from int y in Gen.Choose(5, 10)
where x > 5
select new tuple { Fst = x, Snd = y };
我可以跑
Prop.ForAll<tuple>(c =>
      Console.WriteLine($"{c.Fst}, {c.Snd}")
).Check(Configuration.Default);
我看到了构造生成器和定义属性的所有方法。
但我只是没有足够快地找到如何一起使用它们。
我有一个FsCheck问题:
我有以下记录类型(我事先说,我被告知我的单案例 DU 可能是一种矫枉过正,但我发现它们描述了域,因此是必要的,我会除非必须,否则不要删除它们):
type Name = Name of string
type Quality = Quality of int
type ShelfLife = Days of int
type Style = Plain | Aged | Legendary
type Item = {
    Name: Name
    Quality: Quality
    ShelfLife: ShelfLife
    Style: Style
}
假设我已经定义了函数repeat: Int -> ('a -> 'a) -> 'a和decreaseQuality: Item -> Item,我想编写一个FsCheck检查不变量的测试:任何样式为 OTHER THAN Legendary 的项目,在 100 天过去后,质量为 0。
我的问题是我不知道以下内容FsCheck:
1. 如何定义一个自定义生成器来生成样式不是传奇的项目?相比之下,我如何仅定义Legendary 类型的项目(以测试两种类型)?  
我调查过: …
如何获得使用时产生的运行[<Property>]使用Check.VerboseAll一代的风格?
TL; DR:我无法在C#中成功将FsCheck与NUnit结合使用:
我认为一个虚拟但完整的示例会有所帮助...
(更多细节)
第一步:测试保持绿色
我安装了Nuget软件包FsCheck.NUnit(2.10.4),并且天真地尝试过:
[NUnit.Framework.Test]
public void SomeTest()
{
    // Note the subtle bug: I Reverse only once, because I want the test to fail
    Func<int[],bool> revRevIsOrig = xs => xs.Reverse().SequenceEqual( xs );
    Prop.ForAll(revRevIsOrig).QuickCheck();
}
当我像运行任何NUnit测试一样运行它时,它最终显示为绿色,即使我在stdout上看到
Falsifiable, after 3 tests (1 shrink) (StdGen (2129798881,296376481)):
Original:
[|-1; 0|]
Shrunk:
[|1; 0|]
第二步:测试不确定
所以我继续,找到了一些文档,并注意到我应该使用Property而不是Test。所以我将代码更改为
[FsCheck.NUnit.Property] // <-- the line that changed
public void SomeTest()
{
    Func<int[],bool> revRevIsOrig = xs …当我尝试通过#r“ FsCheck”打开FsCheck命令打开FsCheck时,在.fsx文件中,出现此错误。
尝试使用谷歌搜索,但找不到任何答案可以解决我的问题。
#r“ FsCheck”-打开FsCheck ;;
->引用了“ / * / FsCheck.dll”(文件可能被F#Interactive进程锁定了)