Let's say I'm writing some code for video analysis. Here is a simplified version of a Video class:
public class Video
{
public readonly int Width;
public readonly int Height;
public readonly List<int[,]> Frames;
public Video(int width, int height, IEnumerable<int[,]> frames)
{
Width = width;
Height = height;
Frames = new List<int[,]>();
foreach (var frame in frames)
{
if (frame.GetLength(0) != height || frame.GetLength(1) != width)
{
throw new ArgumentException("Incorrect frames dimensions");
}
Frames.Add(frame);
}
}
}
How do I make an Arbitrary<Video>
and register it? How do I make a shrinker for that Arbitrary?
Tried this, couldn't understand how apply works:
public static Arbitrary<Video> Videos()
{
var videoGen = Arb.Generate<PositiveInt>()
.SelectMany(w => Arb.Generate<PositiveInt>(), (w, h) => new {w, h})
.Apply( /* what is Gen<Func<a,b>> */);
return videoGen.ToArbitrary();
}
Tried this, but couldn't plug the generator for list in here:
public static Arbitrary<Video> Videos()
{
var videoGen = Arb.Generate<PositiveInt>()
.SelectMany(w => Arb.Generate<PositiveInt>(), (w, h) => new Video(w, h, /* how to plug generator here? */));
return videoGen.ToArbitrary();
}
Using Kurt Schelfthout's answer as a foundation, you can write an Arbitrary for the
video
class like this:You can use this in various ways.
Plain vanilla FsCheck
Here's how to use the Video Arbitrary with plain vanilla FsCheck, here hosted within an xUnit.net test case, which isn't required: you can host this in whichever process you prefer:
Prop.ForAll
is very useful for defining properties with custom Arbitraries. When you callQuickCheckThrowOnFailure
, it's going to run the test for 'all' (by defailt: 100) values of theVideo
class.Untyped xUnit.net property
You can also use the FsCheck.Xunit Glue Library, but you have to pass the Arbitrary as a weakly typed value to the attribute:
This is simple and easy to understand, but there's no static type checking involved when assigning that
Arbitrary
property, so I'm not too fond of this approach.Typed xUnit.net property
A better way to use FsCheck.Xunit with custom Arbitraries is to combine it with Prop.ForAll:
Notice that the return type of this method is no longer
void
, butProperty
; the[Property]
attribute understands this type and executes the test accordingly.This third option is my preferred way of using custom Arbitraries from within xUnit.net, because it brings back compile-time checking.