2014-04-22 9 views
7

Utilizzando FsCheck, la versione F # della libreria di test Haskell QuickCheck, per generare test da C#, ho trovato che il generatore di stringhe casuali non genera la stringa nulla.Come generare stringhe null per i test FsCheck

using FsCheck.Fluent; 
Spec.ForAny<string>(s => s != null).QuickCheck(); // always pass 

Inoltre, ci sembra di non gestire stringhe nulle in base alla progettazione, ma non sono riusciti a pin giù from the documentation. Ad esempio, proprio raccogliendo tra due stringhe, uno di loro nulla, non funziona:

var strings = Any.ValueIn<string>(null, "non-null string"); 
Spec.For(strings, s => true).QuickCheck(); // throws null ref exception 

e stringhe sembra essere un caso speciale, perché gestisce gli oggetti su misura, come

class Thing {} 

se mescolato con valori nulli:

var objects = Any.ValueIn(null, new Thing()); 
Spec.For(objects, s => true).QuickCheck(); // pass 

risposta

4

ho cercato di scavare un po 'in questo e sembra che hai scoperto un bug in FsCheck.

Sembra che il problema sia nel file Arbitrary.fs ed è in realtà solo relativo alle stringhe. Ho dovuto sostituire questo, dove lo chiamano ToCharArray sulla corda

static member String() = 
     { new Arbitrary<string>() with 
      override x.Generator = Gen.map (fun chars -> new String(List.toArray chars)) generate 
      override x.Shrinker s = s.ToCharArray() |> Array.toList |> shrink |> Seq.map (fun chars -> new String(List.toArray chars)) 
     } 

con questo

static member String() = 
     { new Arbitrary<string>() with 
      override x.Generator = Gen.map (fun chars -> new String(List.toArray chars)) generate 
      override x.Shrinker s = 
       match s with 
        | null -> seq {yield null;} 
        | _ -> s.ToCharArray() |> Array.toList |> shrink |> Seq.map (fun chars -> new String(List.toArray chars)) 
     } 

Si consiglia di sollevare questo con gli sviluppatori fscheck here e anche controllare se il mio fix funziona bene - non c'è probabilmente un modo migliore per implementarlo, ma sarebbe più semplice per qualcuno, che già conosce il codice.

+0

Se si tratta di un bug, questo potrebbe causare problemi se risolto. Quindi potrebbero aggiornarlo in una "funzione". – Alapago

+1

@ user2046431 Ne dubito fortemente, in quanto questo caso fallirà per e solo per 'stringa' nullo. E anche se decidessero di aggiornarlo a una funzione, potrebbero almeno voler rintracciarla da qualche parte sul loro sito. –

+0

Concordato che questo dovrebbe andare al loro tracker dei problemi, e che il fixing del restringitore in modo che non soffochi sui valori nulli non romperebbe nulla. Stavo pensando al "bug" di non generare nulla per impostazione predefinita. – Alapago

1

Per FsCheck 1.x, ho trovato una soluzione che prevede la modifica del generatore stringa casuale di default:

public class MyArbitraries 
{ 
    public static Arbitrary<string> String() 
    { 
     var nulls = Any.Value<string>(null); 
     var nonnulls = Arb.Default.String().Generator; 
     return Any.GeneratorIn(nulls, nonnulls).ToArbitrary; 
    } 
} 

e quindi inizializzarlo con:

DefaultArbitraries.Add<MyArbitraries>(); 

Poi il test in questione fallisce come previsto:

Spec.ForAny<string>(s => s != null).QuickCheck() // now fails, which is good 

Questo genererà circa il 50% di null e il 50% di stringhe casuali , I pesi possono essere regolati:

Spec.ForAny<string>(s => true) 
    .Classify(s => s==null, "null") 
    .Classify(s => s!=null, "not null") 
    .QuickCheck(); // displays percentages 

Tuttavia, in modo efficace override il generatore di stringa di default potrebbe non essere una buona idea se la decisione di non includere il valore nullo per difetto era intenzionale e non un bug nella libreria. E, se fosse un bug, distorcerebbe le distribuzioni quando riparato.

+0

Non ho alcuna fortuna a trovare "DefaultArbitraries" dov'è quel tizio? – Maslow

+1

Questa risposta è applicabile solo a FsCheck 1.x. In 2.x dovresti usare invece "Arb.register". Inoltre, in 2.x i null sono generati per le stringhe. –

Problemi correlati