【问题标题】:FsCheck in C#: generate a list of two dimension arrays with the same shapeC#中的FsCheck:生成具有相同形状的二维数组列表
【发布时间】:2015-09-29 23:14:42
【问题描述】:

假设我正在编写一些用于视频分析的代码。这是 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&lt;Video&gt; 并注册它?我如何为那个 Arbitrary 做一个收缩器?

试过了,不明白 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();
}

【问题讨论】:

    标签: c# unit-testing fscheck generative-testing


    【解决方案1】:

    使用 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 时,它将运行Video 类的“所有”(默认:100)值的测试。

    无类型的 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);
            });
    }
    

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

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

    【讨论】:

    • 我不知道类型化的 xUnit.net 属性,这是一个不错的选择!
    【解决方案2】:

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

    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);
    

    【讨论】:

    • 这给了我一个良好的开端,但在编译之前我不得不稍微调整一下 :)
    猜你喜欢
    • 1970-01-01
    • 2019-10-23
    • 2014-04-28
    • 1970-01-01
    • 2021-10-21
    • 2015-05-31
    • 1970-01-01
    • 2020-06-29
    • 1970-01-01
    相关资源
    最近更新 更多