[英]Calling default FsCheck generator from a custom generator of the same type
[英]FsCheck: Override generator for a type, but only in the context of a single parent generator
我似乎經常遇到我想要生成一些復雜結構的情況,但是生成的成員類型的特殊變體不同。
例如,考慮這棵樹
type Tree<'LeafData,'INodeData> =
| LeafNode of 'LeafData
| InternalNode of 'INodeData * Tree<'LeafData,'INodeData> list
我想生成這樣的案例
如果我覆蓋相應子類型的所有代,這些很容易做到。 問題是似乎register本質上是一個線程級操作,並且沒有 gen-local 替代方案。
例如,我想要的可能看起來像
let limitedLeafs =
gen {
let leafGen = Arb.generate<LeafType> |> Gen.filter isAllowedLeaf
do! registerContextualArb (leafGen |> Arb.fromGen)
return! Arb.generate<Tree<NodeType, LeafType>>
}
這個 Tree 示例特別可以解決一些創造性的類型改組,但這並不總是可行的。
也可以使用某種強制假設的遞歸映射,但如果上述可能的話,這似乎相對復雜。 不過,我可能會誤解 FsCheck 生成器的性質。
有誰知道如何完成這種 gen-local 覆蓋?
這里有幾個選項 - 我假設您使用的是 FsCheck 2.x,但繼續滾動查看 FsCheck 3 中的選項。
第一個是最自然的,但需要更多的工作,即明確地將生成器分解到您需要的級別,然后再次將 humpty dumpty 放在一起。 即不要太依賴基於類型的生成器派生 - 如果我正確理解你的示例,這將意味着實現遞歸生成器- 依賴Arb.generate<LeafType>
泛型類型。
第二個選項 -Config
有一個Arbitrary
字段,您可以使用它來覆蓋Arbitrary
實例。 即使被覆蓋的類型是自動生成的類型的一部分,這些覆蓋也會生效。 因此,作為草圖,您可以嘗試:
Check.One ({Config.Quick with Arbitrary = [| typeof<MyLeafArbitrary>) |]) (fun safeTree -> ...)
更廣泛的例子,它使用 FsCheck.Xunit 的PropertyAttribute
但原理相同,而是在Config
上設置。
最后的選擇! :) 在 FsCheck 3(預發行版)中,您可以通過一個新的(尚未記錄的)概念ArbMap
來配置它,它使從類型到Arbitrary
實例的映射顯式,而不是 2.x 中的這種靜態全局廢話(我當然不好。當時似乎是一個好主意。)實現在這里可能不會告訴你那么多 - 這個想法是你將一個ArbMap
實例放在一起,其中包含子部分的“安全”生成器,然后你ArbMap.mergeWith
使用ArbMap.defaults
的安全地圖(從而在生成的ArbMap
安全的生成器覆蓋默認生成器),然后將ArbMap.arbitrary
或ArbMap.generate
與生成的地圖一起使用。
很抱歉冗長的解釋——但總而言之,這應該給你兩全其美——你可以在 FsCheck 中重用通用聯合類型生成器,同時在該上下文中通過手術覆蓋某些類型。
FsCheck 對此的指導是:
要定義為現有類型生成正常值范圍的子集的生成器,例如所有偶數整數,如果您定義單例聯合案例並為新類型注冊生成器,它會使屬性更具可讀性:
例如,他們建議您可以像這樣定義任意偶數:
type EvenInt = EvenInt of int with
static member op_Explicit(EvenInt i) = i
type ArbitraryModifiers =
static member EvenInt() =
Arb.from<int>
|> Arb.filter (fun i -> i % 2 = 0)
|> Arb.convert EvenInt int
Arb.register<ArbitraryModifiers>() |> ignore
然后,您可以生成並測試其葉子為偶數的樹,如下所示:
let ``leaves are even`` (tree : Tree<EvenInt, string>) =
let rec leaves = function
| LeafNode leaf -> [leaf]
| InternalNode (_, children) ->
children |> List.collect leaves
leaves tree
|> Seq.forall (fun (EvenInt leaf) ->
leaf % 2 = 0)
Check.Quick ``leaves are even`` // output: Ok, passed 100 tests.
老實說,我更喜歡您關於“本地覆蓋”的想法,但我認為 FsCheck 不支持它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.