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> и зарегистрировать это? Как мне сделать усадку для этого Произвольного?

Пробовал это, не мог понять, как работает приложение:

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

2 ответа

Решение

Используя ответ Курта Шелфтоута в качестве основы, вы можете написать Произвольное для 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 очень полезно для определения свойств с помощью пользовательских Arbitraries. Когда вы звоните QuickCheckThrowOnFailure, он собирается запустить тест для всех (по умолчанию: 100) значений Video учебный класс.

Нетипизированное свойство xUnit.net

Вы также можете использовать библиотеку клея FsCheck.Xunit, но вы должны передать произвольное значение как слабо типизированное значение атрибуту:

[Property(Arbitrary = new[] { typeof(VideoArbitrary) })]
public void XunitPropertyWithWeaklyTypedArbitrary(Video video)
{
    // Test goes here...
    Assert.NotNull(video);
}

Это просто и легко понять, но при назначении Arbitrary собственности, поэтому я не слишком люблю этот подход.

Типизированное свойство xUnit.net

Лучший способ использовать FsCheck.Xunit с пользовательскими Арбитражами - это объединить его с Prop.ForAll:

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

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

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

Просто подгонка моментального наброска - не скомпилирована:)

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);
Другие вопросы по тегам