2017-06-28 5 views
3

Ich versuche zu lernen, wie man FsCheck richtig verwendet und es mit Expecto im Moment integriert. Ich kann Eigenschaftstests ausführen, wenn ich die standardmäßige FsCheck-Konfiguration verwende, aber wenn ich versuche, meinen eigenen Generator zu verwenden, verursacht dies eine Stapelüberlauf-Ausnahme.Expecto FsCheck bekomme Stapelüberlauf Ausnahme beim Generieren von String

Hier ist mein Generator

type NameGen() = 
    static member Name() = 
     Arb.generate<string * string> 
     |> Gen.where (fun (firstName, lastName) -> 
      firstName.Length > 0 && lastName.Length > 0 
     ) 
     |> Gen.map (fun (first, last) -> sprintf "%s %s" first last) 
     |> Arb.fromGen 
     |> Arb.convert string id 

Und ich versuche es so zu verwenden:

let config = { FsCheckConfig.defaultConfig with arbitrary = [typeof<NameGen>] } 

let propertyTests input = 
    let output = toInitials input 
    output.EndsWith(".") 

testPropertyWithConfig config "Must end with period" propertyTests 

Die Ausnahme geworfen wird, bevor es in die Gen.where Funktion

wird noch

Was mache ich falsch? Danke

Antwort

5

Sie versuchen, FsChecks Generator von Zeichenfolgen zu verwenden, um neu zu definieren, wie sein Generator von Zeichenfolgen funktioniert, aber wenn Sie das tun, wird es rekursiv sich selbst aufrufen, bis es nicht mehr über Stapelspeicher verfügt. Es ist ein bekanntes Problem: https://github.com/fscheck/FsCheck/issues/109

Funktioniert diese Alternative?

type NameGen = 
    static member Name() = 
     Arb.Default.NonEmptyString().Generator 
     |> Gen.map (fun (NonEmptyString s) -> s) 
     |> Gen.two 
     |> Gen.map (fun (first, last) -> sprintf "%s %s" first last) 
     |> Arb.fromGen 
3

Sie definieren einen neuen Generator für den Typ String, aber innerhalb, dass Sie den Generator für string * string verwenden, die den Generator für string verwendet. FsCheck scheint leider Generatoren im globalen veränderbaren Zustand zu speichern (vielleicht aus gutem Grund?) Und ich denke, das bedeutet, dass der Generator sich selbst so lange aufruft, bis der Stack überläuft.

Sie könnten dies lösen, indem Sie den Generator für einen benutzerdefinierten Wrapper-Typ anstelle einer einfachen Zeichenfolge definieren (siehe unten).

Das nächste Problem, das Sie treffen, ist Null Referenz Ausnahmen. Die anfängliche generierte Zeichenfolge könnte null lauten und Sie versuchen, auf die .Length-Eigenschaft zuzugreifen. Dies kann mithilfe der String.length-Funktion behoben werden, die 0 für null zurückgibt.

Mit diesen Änderungen Ihr Generator sieht wie folgt aus:

type Name = Name of string 

type NameGen() = 
    static member Name() = 
     Arb.generate<string * string> 
     |> Gen.where (fun (firstName, lastName) -> 
      String.length firstName > 0 && String.length lastName > 0 
     ) 
     |> Gen.map (fun (first, last) -> sprintf "%s %s" first last) 
     |> Arb.fromGen 
     |> Arb.convert Name (fun (Name n) -> n) 

Und Ihre Immobilie braucht eine leichte Modifikation:

let propertyTests (Name input) = 
    let output = toInitials input 
    output.EndsWith(".") 
Verwandte Themen