2015-06-26 10 views
7

consideri un'Unione Discriminazione in:Come filtrare facilmente un caso sindacale discriminato in FsCheck?

type DU = | Foo of string | Bar of int | Baz of decimal * float | Qux of bool 

Mi piacerebbe creare un elenco di valori con DU FsCheck, ma voglio nessuno dei valori sia del caso Qux.

Questo predicato esiste già:

let isQux = function Qux _ -> true | _ -> false 

Primo tentativo

Il mio primo tentativo di creare un elenco di DU valori senza il caso Qux era qualcosa di simile:

type DoesNotWork = 
    static member DU() = Arb.from<DU> |> Arb.filter (not << isQux) 

[<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesNotWork> |])>] 
let repro (dus : DU list) = 
    printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus 

L'esecuzione di questo sembra produrre uno stack overflow, quindi presumo che ciò che ha ppens dietro la scena è che Arb.from<DU> chiama DoesNotWork.DU.

Secondo tentativo

Poi ho provato questo:

type DoesNotWorkEither = 
    static member DU() = 
     Arb.generate<DU> 
     |> Gen.suchThat (not << isQux) 
     |> Arb.fromGen 

[<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesNotWorkEither> |])>] 
let repro (dus : DU list) = 
    printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus 

Stesso problema di cui sopra.

soluzione dettagliato

Questa è la soluzione migliore che ho potuto venire con finora:

type WithoutQux = 
    static member DU() = 
     [ 
      Arb.generate<string> |> Gen.map Foo 
      Arb.generate<int> |> Gen.map Bar 
      Arb.generate<decimal * float> |> Gen.map Baz 
     ] 
     |> Gen.oneof 
     |> Arb.fromGen 

[<Property(MaxTest = 10 , Arbitrary = [| typeof<WithoutQux> |])>] 
let repro (dus : DU list) = 
    printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus 

Questo funziona, ma ha i seguenti svantaggi:

  • Sembra un sacco di lavoro
  • Non utilizza il già disponibileFunzione, quindi sembra sottilmente violare DRY
  • Non è proprio il filtro , ma produce solo i casi desiderati (quindi filtri solo per omissione).
  • Non è particolarmente gestibile, perché se aggiungo mai un quinto caso a DU, dovrei ricordare aggiungere anche uno Gen per quel caso.

C'è un modo più elegante per dire a FsCheck di filtrare i valori Qux?

risposta

5

Invece di Arb.generate, che cerca di utilizzare la istanza registrata per il tipo, che è l'istanza che si sta tentando di definire, che causa un ciclo infinito, utilizzare Arb.Default.Derive() che andrà direttamente al generatore basato su riflettente.

https://github.com/fscheck/FsCheck/blob/master/src/FsCheck/Arbitrary.fs#L788-788

Questo è un errore tanto comune dovremmo essere in grado di risolvere fuori dalla scatola in FsCheck: https://github.com/fscheck/FsCheck/issues/109


Il problema particolare nel PO può essere risolto in questo modo:

4

Il sotto dovrebbe funzionare:

type DU = | Foo of string | Bar of int | Baz of decimal * float | Qux of bool 
let isQux = function Qux _ -> true | _ -> false 

let g = Arb.generate<DU> |> Gen.suchThat (not << isQux) |> Gen.listOf 

type DoesWork = 
    static member DU() = Arb.fromGen g 

[<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesWork> |])>] 
let repro (dus : DU list) = 
    printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus 

Nota ho usato Gen.listOf alla fine - sembra FsCheck non riesce a generare in sé una lista con il generatore dato

+0

Bene, questo aggira il problema creando un 'Arbitrario ' invece di un 'Arbitrario ', che può ve il mio problema immediato, ma non sembra affrontare il problema di fondo. –

+0

Questo non pone il problema che non otteniamo il supporto restringente per il DU? – Henrik