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();
}
FsCheck is a tool for testing . NET programs automatically. The programmer provides a specification of the program, in the form of properties which functions, methods or objects should satisfy, and FsCheck then tests that the properties hold in a large number of randomly generated cases.
Using Kurt Schelfthout's answer as a foundation, you can write an Arbitrary for the video
class like this:
public static class VideoArbitrary
{
public static Arbitrary<Video> Videos()
{
var genVideo = from w in Arb.Generate<PositiveInt>()
from h in Arb.Generate<PositiveInt>()
from arrs in Gen.ListOf(
Gen.Array2DOf<int>(
h.Item,
w.Item,
Arb.Generate<int>()))
select new Video(w.Item, h.Item, arrs);
return genVideo.ToArbitrary();
}
}
You can use this in various ways.
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:
[Fact]
public void VideoProperty()
{
var property = Prop.ForAll(
VideoArbitrary.Videos(),
video =>
{
// Test goes here...
Assert.NotNull(video);
});
property.QuickCheckThrowOnFailure();
}
Prop.ForAll
is very useful for defining properties with custom Arbitraries. When you call QuickCheckThrowOnFailure
, it's going to run the test for 'all' (by defailt: 100) values of the Video
class.
You can also use the FsCheck.Xunit Glue Library, but you have to pass the Arbitrary as a weakly typed value to the attribute:
[Property(Arbitrary = new[] { typeof(VideoArbitrary) })]
public void XunitPropertyWithWeaklyTypedArbitrary(Video video)
{
// Test goes here...
Assert.NotNull(video);
}
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.
A better way to use FsCheck.Xunit with custom Arbitraries is to combine it with Prop.ForAll:
[Property]
public Property XUnitPropertyWithStronglyTypedArbitrary()
{
return Prop.ForAll(
VideoArbitrary.Videos(),
video =>
{
// Test goes here...
Assert.NotNull(video);
});
}
Notice that the return type of this method is no longer void
, but Property
; 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.
Just a spur of the moment sketch - not compiled :)
var genVideo = from w in Arb.Generate<PositiveInt>()
from h in Arb.Generate<PositiveInt>()
from arrs in Gen.ListOf(Gen.Array2DOf(h, w, Arb.Generate<int>))
select new Video(w, h, arrs);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With