C#中的FsCheck:生成具有相同形状的二维数组列表
FsCheck in C#: generate a list of two dimension arrays with the same shape
假设我正在编写一些视频分析代码。这是视频的简化版本 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);
}
}
}
如何制作 Arbitrary<Video>
并注册?如何为 Arbitrary 制作收缩器?
试过这个,不明白应用是如何工作的:
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();
}
试过了,但无法在此处插入列表生成器:
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();
}
只是一时兴起草图 - 未编译 :)
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);
以 Kurt Schelfthout 的回答为基础,您可以像这样为 video
class 编写一个 Arbitrary:
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();
}
}
您可以通过多种方式使用它。
普通版 FsCheck
以下是如何使用带有普通 FsCheck 的 Video Arbitrary,此处托管在 xUnit.net 测试用例中,这不是必需的:您可以在您喜欢的任何过程中托管它:
[Fact]
public void VideoProperty()
{
var property = Prop.ForAll(
VideoArbitrary.Videos(),
video =>
{
// Test goes here...
Assert.NotNull(video);
});
property.QuickCheckThrowOnFailure();
}
Prop.ForAll
对于使用自定义 Arbitraries 定义属性非常有用。当您调用 QuickCheckThrowOnFailure
时,它将 运行 测试 'all'(默认情况下:100)Video
class.
的值
未键入 xUnit.net 属性
您也可以使用 FsCheck.Xunit Glue 库,但您必须将 Arbitrary 作为弱类型值传递给属性:
[Property(Arbitrary = new[] { typeof(VideoArbitrary) })]
public void XunitPropertyWithWeaklyTypedArbitrary(Video video)
{
// Test goes here...
Assert.NotNull(video);
}
这个方法简单易懂,但是在赋值Arbitrary
属性时没有涉及静态类型检查,所以我不太喜欢这种方法。
键入 xUnit.net 属性
将 FsCheck.Xunit 与自定义 Arbitraries 一起使用的更好方法是 combine it with Prop.ForAll:
[Property]
public Property XUnitPropertyWithStronglyTypedArbitrary()
{
return Prop.ForAll(
VideoArbitrary.Videos(),
video =>
{
// Test goes here...
Assert.NotNull(video);
});
}
注意这个方法的return类型不再是void
,而是Property
; [Property]
属性理解此类型并相应地执行测试。
这第三个选项是我在 xUnit.net 中使用自定义 Arbitraries 的首选方式,因为它带回了编译时检查。
假设我正在编写一些视频分析代码。这是视频的简化版本 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);
}
}
}
如何制作 Arbitrary<Video>
并注册?如何为 Arbitrary 制作收缩器?
试过这个,不明白应用是如何工作的:
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();
}
试过了,但无法在此处插入列表生成器:
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();
}
只是一时兴起草图 - 未编译 :)
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);
以 Kurt Schelfthout 的回答为基础,您可以像这样为 video
class 编写一个 Arbitrary:
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();
}
}
您可以通过多种方式使用它。
普通版 FsCheck
以下是如何使用带有普通 FsCheck 的 Video Arbitrary,此处托管在 xUnit.net 测试用例中,这不是必需的:您可以在您喜欢的任何过程中托管它:
[Fact]
public void VideoProperty()
{
var property = Prop.ForAll(
VideoArbitrary.Videos(),
video =>
{
// Test goes here...
Assert.NotNull(video);
});
property.QuickCheckThrowOnFailure();
}
Prop.ForAll
对于使用自定义 Arbitraries 定义属性非常有用。当您调用 QuickCheckThrowOnFailure
时,它将 运行 测试 'all'(默认情况下:100)Video
class.
未键入 xUnit.net 属性
您也可以使用 FsCheck.Xunit Glue 库,但您必须将 Arbitrary 作为弱类型值传递给属性:
[Property(Arbitrary = new[] { typeof(VideoArbitrary) })]
public void XunitPropertyWithWeaklyTypedArbitrary(Video video)
{
// Test goes here...
Assert.NotNull(video);
}
这个方法简单易懂,但是在赋值Arbitrary
属性时没有涉及静态类型检查,所以我不太喜欢这种方法。
键入 xUnit.net 属性
将 FsCheck.Xunit 与自定义 Arbitraries 一起使用的更好方法是 combine it with Prop.ForAll:
[Property]
public Property XUnitPropertyWithStronglyTypedArbitrary()
{
return Prop.ForAll(
VideoArbitrary.Videos(),
video =>
{
// Test goes here...
Assert.NotNull(video);
});
}
注意这个方法的return类型不再是void
,而是Property
; [Property]
属性理解此类型并相应地执行测试。
这第三个选项是我在 xUnit.net 中使用自定义 Arbitraries 的首选方式,因为它带回了编译时检查。