如何使用FsCheck?实现多参数生成
我实现了以下功能以支持多个参数生成:
// Setup
let pieces = Arb.generate<Piece> |> Gen.filter (isKing >> not)
|> Arb.fromGen
let positionsList = Arb.generate<Space list> |> Arb.fromGen然后,我使用这些参数来测试函数的行为,该函数负责为给定的检查器生成移动选项:
// Test
Prop.forAll pieces <| fun piece ->
Prop.forAll positionsList <| fun positionsItem ->
positionsItem |> optionsFor piece
|> List.length <= 2在管理多个生成的参数类型时,嵌套Prop.forAll表达式是正确的技术吗?
是否有一种为被测试函数生成多个参数的替代方法?
,这是整个函数:
open FsCheck
open FsCheck.Xunit
[<Property(QuietOnSuccess = true)>]
let ``options for soldier can never exceed 2`` () =
// Setup
let pieces = Arb.generate<Piece> |> Gen.filter (isKing >> not)
|> Arb.fromGen
let positionsList = Arb.generate<Space list> |> Arb.fromGen
// Test
Prop.forAll pieces <| fun piece ->
Prop.forAll positionsList <| fun positionsItem ->
positionsItem |> optionsFor piece
|> List.length <= 2更新
下面是从Mark的回答中得到的对我问题的解决方案:
[<Property(QuietOnSuccess = true, MaxTest=100)>]
let ``options for soldier can never exceed 2`` () =
// Setup
let pieceGen = Arb.generate<Piece> |> Gen.filter (isKing >> not)
let positionsGen = Arb.generate<Space list>
// Test
(pieceGen , positionsGen) ||> Gen.map2 (fun x y -> x,y)
|> Arb.fromGen
|> Prop.forAll <| fun (piece , positions) ->
positions |> optionsFor piece
|> List.length <= 2发布于 2016-08-09 02:46:39
作为一般观察,Arbitrary值很难组合,而Gen值则很容易合成。因此,我倾向于用Gen<'a>而不是Arbitrary<'a>来定义我的Gen<'a>构建块。
使用Gen值,可以使用Gen.map2、Gen.map3等组合多个参数,也可以使用gen计算表达式。
Gen构建块
在OP示例中,不要将pieces和positionsList定义为Arbitrary,而是将它们定义为Gen值:
let genPieces = Arb.generate<Piece> |> Gen.filter (isKing >> not)
let genPositionsList = Arb.generate<Space list>这些是Gen<Piece>和Gen<Space list>类型的“构建块”。
请注意,我将它们命名为genPieces,而不是简单的pieces等等。这可以防止以后的名称冲突(见下文)。(而且,我也不确定pieces中复数s的用法,因为genPieces只生成一个Piece值,但是由于我不知道您的整个域,所以我决定保持原样。)
如果您只需要其中之一,则可以使用Arbitrary将其转换为Arb.fromGen。
如果需要组合它们,可以使用一个映射函数或计算表达式,如下所示。这将给您一个元组的Gen,然后可以使用Arb.fromGen将其转换为Arbitrary。
用编写map2
如果需要将pieces和positionsList组合成参数列表,则可以使用Gen.map2
Gen.map2 (fun x y -> x, y) genPieces genPositionList
|> Arb.fromGen
|> Prop.forAll <| fun (pieces, positionList) ->
// test goes here...Gen.map2 (fun x y -> x, y)返回一个双元素元组(一对)值,您可以在匿名函数中将其重构为(pieces, positionList)。
这个示例还应该说明为什么genPieces和genPositionList是Gen值的更好名称:它们为传递给测试主体的生成值留出空间使用“裸”名称pieces和positionList。
使用计算表达式编写
对于更复杂的组合,我有时更喜欢的另一种选择是使用gen计算表达式。
上面的例子也可以这样写:
gen {
let! pieces = genPieces
let! positionList = genPositionList
return pieces, positionList }
|> Arb.fromGen
|> Prop.forAll <| fun (pieces, positionList) ->
// test goes here...初始的gen表达式也返回一对,因此它等同于使用Gen.map2进行组合。
您可以使用您认为最易读的选项。
您可以在我的文章“Gen”中看到更多关于非平凡Gen组合的示例。
https://stackoverflow.com/questions/38839721
复制相似问题