2015-09-27 9 views
12

Diciamo che sto scrivendo del codice per l'analisi video. Ecco una versione semplificata di una classe del video:FsCheck in C#: genera un elenco di array di due dimensioni con la stessa forma

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

Come faccio a fare un Arbitrary<Video> e registrarlo? Come faccio a fare un shrinker per quell'arbitrario?

provato a farlo, non riusciva a capire come applicare opere:

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

provato a farlo, ma non poteva collegare il generatore per la lista qui:

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

risposta

12

Utilizzando la risposta di Kurt Schelfthout come una fondazione, è possibile scrivere un arbitrario fo r la classe video in questo modo:

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

È possibile utilizzare questo in vari modi.

Plain vanilla FsCheck

Ecco come utilizzare il video arbitrari con FsCheck plain vanilla, qui ospitato all'interno di un banco di prova xUnit.net, che non è richiesto: è possibile ospitare questo in qualsiasi processo che preferisci:

[Fact] 
public void VideoProperty() 
{ 
    var property = Prop.ForAll(
     VideoArbitrary.Videos(), 
     video => 
     { 
      // Test goes here... 
      Assert.NotNull(video); 
     }); 
    property.QuickCheckThrowOnFailure(); 
} 

Prop.ForAll è molto utile per definire le proprietà con Arbitrari personalizzate. Quando chiami QuickCheckThrowOnFailure, eseguirà il test per i valori 'all' (per defailt: 100) della classe Video.

proprietà xUnit.net non tipizzato

È inoltre possibile utilizzare la colla Biblioteca FsCheck.Xunit, ma si deve passare l'arbitraria come un valore debolmente tipizzato all'attributo:

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

questo è semplice e facile da capire, ma non c'è nessun controllo di tipo statico coinvolto quando si assegna quella proprietà Arbitrary, quindi non mi piace troppo questo approccio.

tipizzati xUnit.net proprietà

Un modo migliore per utilizzare FsCheck.Xunit con personalizzato Arbitrari è quello di combine it with Prop.ForAll:

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

Si noti che il tipo di ritorno di questo metodo non è più void, ma Property ; l'attributo [Property] comprende questo tipo ed esegue il test di conseguenza.

Questa terza opzione è il mio modo preferito di utilizzare Arbitrari personalizzati all'interno di xUnit.net, perché riporta il controllo in fase di compilazione.

+2

Non conoscevo la proprietà xUnit.net digitata, questa è una grande opzione! –

6

Solo un impulso del momento sketch - non compilato :)

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

Questo mi ha dato un buon inizio, ma ho dovuto smanettare un po 'prima che fosse compilato :) –