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