如何使用 FsCheck 实现多参数生成?

2024-02-01

如何使用 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

UPDATE

这是我的问题的解决方案,源自马克的回答:

[<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

作为一般观察,Arbitrary值很难组合,而Gen价值观很容易。出于这个原因,我倾向于根据以下方面来定义我的 FsCheck 构建块:Gen<'a>代替Arbitrary<'a>.

With Gen值,您可以使用组合多个参数Gen.map2, Gen.map3等,或者您可以使用gen计算表达式。

Gen 积木

在OP示例中,不是定义pieces and positionsList as Arbitrary,将它们定义为Gen values:

let genPieces = Arb.generate<Piece> |> Gen.filter (isKing >> not)

let genPositionsList = Arb.generate<Space list>

这些是类型的“构建块”Gen<Piece> and Gen<Space list>, 分别。

请注意我给它们起了名字genPieces而不是简单地pieces, 等等。这可以防止以后发生名称冲突(见下文)。 (另外,我不确定复数的使用s in pieces, 因为genPieces只生成一个Piece值,但由于我不知道您的整个域,所以我决定保持原样。)

如果您只需要其中之一,可以将其转换为Arbitrary using Arb.fromGen.

如果需要组合它们,可以使用映射函数或计算表达式之一,如下所示。这会给你一个Gen元组,然后你可以使用Arb.fromGen将其转换为Arbitrary.

使用map2编写

如果您需要作曲pieces and 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)返回一个二元素元组(apair)的值,您可以将其解构为(pieces, positionList)在匿名函数中。

这个例子也应该可以清楚地说明原因genPieces and genPositionList是更好的名字Gen价值观:他们为使用“裸名”留出空间pieces and 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我文章中的组合通过基于属性的 TDD 的罗马数字 http://blog.ploeh.dk/2016/06/28/roman-numerals-via-property-based-tdd.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 FsCheck 实现多参数生成? 的相关文章

随机推荐