13

假设我正在编写一些用于视频分析的代码。这是 Video 类的简化版本:

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>并注册它?我如何为那个任意制作收缩器?

试过这个,不明白 apply 是如何工作的:

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();
}
4

2 回答 2

15

使用 Kurt Schelfthout 的答案作为基础,您可以video像这样为该类编写一个 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

以下是如何将 Video Arbitrary 与普通的 FsCheck 一起使用,这里托管在 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,它将对类的“所有”(默认值:100)值运行测试Video

无类型的 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 一起使用的更好方法是将其与 Prop.ForAll 结合使用

[Property]
public Property XUnitPropertyWithStronglyTypedArbitrary()
{
    return Prop.ForAll(
        VideoArbitrary.Videos(),
        video =>
        {
            // Test goes here...
            Assert.NotNull(video);
        });
}

请注意,此方法的返回类型不再是void,而是Property;该[Property]属性理解这种类型并相应地执行测试。

这第三个选项是我在 xUnit.net 中使用自定义 Arbitraries 的首选方式,因为它带回了编译时检查。

于 2015-09-29T11:48:54.900 回答
6

只是一时兴起的草图-未编译:)

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);
于 2015-09-28T14:08:30.093 回答