FsCheck в C#: создать список из двух массивов измерений с одинаковой формой

Допустим, я пишу код для анализа видео. Вот упрощенная версия класса 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();
}

person Pavel Murygin    schedule 27.09.2015    source источник


Ответы (2)


Используя ответ Курта Шелфтаута в качестве основы, вы можете написать Arbitrary для класса video следующим образом:

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 очень удобен для определения свойств с пользовательскими произвольными элементами. Когда вы вызываете 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. Для всех:

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

Обратите внимание, что возвращаемый тип этого метода больше не void, а Property; атрибут [Property] понимает этот тип и соответственно выполняет тест.

Этот третий вариант — мой предпочтительный способ использования пользовательских Arbitraries из xUnit.net, потому что он возвращает проверку во время компиляции.

person Mark Seemann    schedule 29.09.2015
comment
Я не знал о типизированном свойстве xUnit.net, это отличный вариант! - person Erik Schierboom; 29.09.2015

Просто спонтанный скетч - не скомпилированный :)

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);
person Kurt Schelfthout    schedule 28.09.2015
comment
Это дало мне хорошее начало, но мне пришлось немного пошевелить его, прежде чем он скомпилировался :) - person Mark Seemann; 29.09.2015