Can these Arbitraries (gen+shrinker) be simplified?
See original GitHub issueI’m trying to implement Arbitraries (gens+shrinkers) for some constrained types, such as CloseDateInterval
and NonEmptyNonWhitespaceString
. I find the documentation a bit confusing and lacking in examples. I have something that seems to work, but I have no idea if this is the best way to do it.
I’d simply like anyone to take a quick look and tell me if I’m on the right track or if, as I suspect, I’ve missed some more elegant way of doing it.
Here’s the code for CloseDateInterval
:
/// A tuple with two dates no more than 500 days apart
type CloseDateInterval = CloseDateInterval of DateTime * DateTime with
// Do I need to implement this? What happens if not?
static member op_Explicit(CloseDateInterval (x1, x2)) = (x1, x2)
let private gen2CloseDateTimes maxDaysApart =
gen {
let! d1 = Arb.generate<DateTime>
let! d2 = Arb.generate<DateTime>
let d2 = DateTime(d1.Year, d1.Month, d1.Day, d2.Hour, d2.Minute, d2.Second)
let! daysApart = Gen.choose (-maxDaysApart, maxDaysApart)
return (d1, d2.AddDays(float daysApart))
}
type ArbitraryAdditions =
static member CloseDateInterval() =
let shrinker (d1, d2) =
let shrink = Arb.Default.DateTime().Shrinker
(shrink d1, shrink d2) ||> Seq.zip
Arb.fromGenShrink ((gen2CloseDateTimes 500), shrinker)
|> Arb.convert CloseDateInterval (fun (CloseDateInterval (d1, d2)) -> (d1, d2))
And here’s the code for NonEmptyNonWhitespaceString
:
type NonEmptyNonWhitespaceString = NonEmptyNonWhitespaceString of string with
// Do I need to implement this? What happens if not?
static member op_Explicit(NonEmptyNonWhitespaceString s) = s
type ArbitraryAdditions =
static member NonEmptyNonWhitespaceString() =
let filter s =
not (isNull s) && s <> "" && s |> Seq.forall (not << System.Char.IsWhiteSpace)
let arb =
Arb.from<string>
|> Arb.filter filter
|> Arb.convert NonEmptyNonWhitespaceString string
let shrinker (NonEmptyNonWhitespaceString s) =
let shrink = Arb.Default.String().Shrinker
shrink s |> Seq.filter filter |> Seq.map NonEmptyNonWhitespaceString
Arb.fromGenShrink (arb.Generator, shrinker)
Whenever I need these types generated, I simply add
[<Properties(Arbitrary = [|typeof<TestHelpers.ArbitraryAdditions>|])>]
to the module containing the tests, or using the Property
attribute if I only need it on a single test. (I know you can also register them, but I’m having trouble with that, see #390.)
Issue Analytics
- State:
- Created 6 years ago
- Comments:21 (12 by maintainers)
Top GitHub Comments
Perhaps we should simplify that example so that it doesn’t confuse newcomers. It can be rewritten like this:
For that particular example, you can just let FsCheck generate any
ints : int list
, and then partition them: