2017-06-28 73 views
3

我試圖學習如何正確使用FsCheck,並將它與Expecto整合在一起。如果我使用默認的FsCheck配置,我可以運行屬性測試,但是當我嘗試使用自己的Generator時,會導致堆棧溢出異常。Expecto FsCheck在生成字符串時出現堆棧溢出異常

這裏是我的發電機

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 

而且我試圖用這樣的:

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

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

testPropertyWithConfig config "Must end with period" propertyTests 

拋出異常甚至進入了Gen.where函數之前

什麼我做錯了嗎?謝謝

回答

5

你正試圖使用​​FsCheck的字符串生成器來重新定義它的字符串生成器是如何工作的,但是當你這樣做時,它會遞歸地調用它自己,直到它用完堆棧空間。這是一個已知的問題:https://github.com/fscheck/FsCheck/issues/109

做這種替代的工作?

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

要定義類型字符串的新發電機,但內您使用的是string * string發電機,它使用發電機string。不幸的是,FsCheck似乎將生成器存儲爲全局可變狀態(可能有很好的理由?),我認爲這意味着生成器會一直調用自己,直到堆棧溢出。

您可以通過爲自定義包裝類型而不是普通字符串(如下所示)定義生成器來解決此問題。

您將遇到的下一個問題將爲空參考例外。初始生成的字符串可能是null,並且您嘗試訪問.Length屬性。這可以使用String.length函數來解決,該函數返回0null

隨着這些變化的發生是這樣的:

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) 

而且你的財產需要稍作修改:

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