FsCheck:如何生成依赖于其他测试数据的测试数据?

Goo*_*ide 4 c# fscheck property-based-testing

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();
}
Run Code Online (Sandbox Code Playgroud)

尽管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();
Run Code Online (Sandbox Code Playgroud)

Lee*_*Lee 5

您可以使用来创建依赖于另一个生成器的值的生成器SelectMany。这也允许您使用LINQ查询语法,例如

var gen = from s in Arb.Generate<NonEmptyString>()
          from i in Gen.Choose(0, s.Get.Length - 1)
          select Tuple.Create(s, i);

var p = Prop.ForAll(Arb.From(gen), t =>
{
    var s = t.Item1.Get;
    var len = t.Item2;
    return s.Substring(0, len).Length <= s.Length;
});

Check.Quick(p);
Run Code Online (Sandbox Code Playgroud)

  • @GoodNightNerdPride-我认为我没有看到太多有关C#互操作的文档,因此我可能是基于阅读源代码以及来自gen生成器的有根据的猜测而发现的。F#计算表达式(例如`gen {...}`)非常接近C#LINQ查询语法,因为它们是基于相同的语法模式构建的(尽管F#支持更多功能)。 (2认同)
  • 对于F#计算表达式或LINQ语法,文档中没有太多示例。这是一个示例https://github.com/fscheck/FsCheck/blob/master/examples/FsCheck.CSharpExamples/Program.cs#L150在TestData页面的第二段中提到了LINQ,这里有一个小示例: https://fscheck.github.io/FsCheck/TestData.html#Generators (2认同)