FsCheck has some neat default Arbitrary
types to generate test data. However what if one of my test dates depends on another?
For instance, consider the property of string.Substring()
that a resulting substring can never be longer than the input string:
[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();
}
Although the implementation of Substring
certainly is correct, this property fails, because eventually a PositiveInt
will be generated that is longer than the genereated NonEmptyString
resulting in an exception.
Shrunk: NonEmptyString "a" PositiveInt 2 with exception: System.ArgumentOutOfRangeException: Index and length must refer to a location within the string.
I could guard the comparison with an if (input.Length < length) return true;
but that way I end up with lots of test runs were the property isn't even checked.
How do I tell FsCheck to only generate PositiveInt
s that don't exceed the input string? I presume I have to use the Gen<T>
class, but it's interface is just hella confusing to me... I tried the following but still got PositiveInt
s exceeding the string:
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();