首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用FsCheck实现多参数生成?

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

Stack Overflow用户
提问于 2016-08-08 22:58:56
回答 1查看 405关注 0票数 4

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

我实现了以下功能以支持多个参数生成:

代码语言:javascript
复制
// Setup
let pieces =    Arb.generate<Piece> |> Gen.filter (isKing >> not)
                                    |> Arb.fromGen  

let positionsList = Arb.generate<Space list> |> Arb.fromGen

然后,我使用这些参数来测试函数的行为,该函数负责为给定的检查器生成移动选项:

代码语言:javascript
复制
// Test
Prop.forAll pieces <| fun piece ->
    Prop.forAll positionsList <| fun positionsItem ->

        positionsItem |> optionsFor piece 
                      |> List.length <= 2

在管理多个生成的参数类型时,嵌套Prop.forAll表达式是正确的技术吗?

是否有一种为被测试函数生成多个参数的替代方法?

,这是整个函数:

代码语言:javascript
复制
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的回答中得到的对我问题的解决方案:

代码语言:javascript
复制
[<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
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-08-09 02:46:39

作为一般观察,Arbitrary值很难组合,而Gen值则很容易合成。因此,我倾向于用Gen<'a>而不是Arbitrary<'a>来定义我的Gen<'a>构建块。

使用Gen值,可以使用Gen.map2Gen.map3等组合多个参数,也可以使用gen计算表达式。

Gen构建块

在OP示例中,不要将piecespositionsList定义为Arbitrary,而是将它们定义为Gen值:

代码语言:javascript
复制
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

如果需要将piecespositionsList组合成参数列表,则可以使用Gen.map2

代码语言:javascript
复制
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)

这个示例还应该说明为什么genPiecesgenPositionListGen值的更好名称:它们为传递给测试主体的生成值留出空间使用“裸”名称piecespositionList

使用计算表达式编写

对于更复杂的组合,我有时更喜欢的另一种选择是使用gen计算表达式。

上面的例子也可以这样写:

代码语言:javascript
复制
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组合的示例。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38839721

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档