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
,其长度超过生成的NonEmptyString
导致异常。
收缩:NonEmptyString&#34; a&#34; 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();
答案 0 :(得分:4)
您可以使用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);