2

大規模なプロジェクトのベスト プラクティスを確立するために、判別共用体を使用する型の FsCheck の例を現在試しています。現在、ジェネレーターから null を取得していますが、その理由がわかりません。次のコードでは、DataGen.containerGenerator が null です。

namespace Container
open System
open Xunit
open FsCheck

module ContainerLibrary = 
    type [<Measure>] oz

    type Container = 
        | Cup of Common
        | Bowl of Common
    and Common = 
        { Volume    :decimal<oz>
          Weight    :decimal}

module DataGen = 
    type Generators = 
        static member arbVolume = 
            FsCheck.Gen.choose (1, 16)
            |> FsCheck.Gen.map(fun x -> (decimal x / 8.0M) * 1.0M<ContainerLibrary.oz>)
            |> FsCheck.Arb.fromGen

    FsCheck.Arb.register<Generators>() |> ignore

    let bowlGenerator = 
        FsCheck.Gen.map2 (fun a b -> ContainerLibrary.Bowl( { Volume = a 
                                                              Weight = b})) 
                         (Generators.arbVolume.Generator) 
                         (FsCheck.Arb.generate<decimal>)
    let cupGenerator =
        FsCheck.Gen.map2 (fun a b -> ContainerLibrary.Cup( { Volume = a 
                                                             Weight = b})) 
                         (Generators.arbVolume.Generator) 
                         (FsCheck.Arb.generate<decimal>)

    let containerGenerator =
        Gen.oneof [bowlGenerator; cupGenerator]

module Tests =
    [<Fact;>]
    let ``01 : Containers must be no more than 20 oz`` () =
        //Is this the best way to get one of something?
        let c = FsCheck.Gen.sample 0 1 DataGen.containerGenerator |> Seq.head
        Assert.NotNull (c)
4

1 に答える 1

2

より多くの値を取得しても、実行するとnullではないようです。どのバージョンの FsCheck を使用していますか?

[<Fact;>]
let ``01 : Containers must be no more than 20 oz`` () =
    //Is this the best way to get one of something?
    Gen.sample 0 100 DataGen.containerGenerator |> Seq.iter(fun c -> printf "%A" c; Assert.NotNull (c))

いずれにせよ、あなたがしていることについて注意すべき点がいくつかあります。

  • FsCheck はリフレクションを使用してジェネレーターを登録します。およびメジャー タイプ パラメータのタイプは、リフレクションでは見ることができません。したがって、Arb.register は、すべての小数について、実際には小数ジェネレーターをオーバーライドします。
  • どういうわけかFsCheck。あなたが使用した資格は混乱したインテリセンスの終わりではありません。
  • Gen.sample はジェネレーターをテストする合理的な方法ですが、私は主にインタラクティブな設定で使用します。テストのセットアップに苦労したことがあるなら、私は FsCheck に組み込まれているテスト ケース監視機能を使用する傾向があります。こちらの「テストケース配布の観察」を参照してください: https://fsharp.github.io/FsCheck/Properties.html
  • ジェネレーターを登録するための F# のモジュール初期化規則に応じて、モジュール init で Arb.register を使用するのは少し脆弱です。Xunit を使用している場合は、組み込みの統合を使用して、この分野で避けられないフラストレーションを軽減することをお勧めします。

これらのことのいくつかを考慮して、あなたの例を少し書き直しました。

module DataGen = 

open ContainerLibrary

//can't really register this one because of the measure, would override all decimal generatos
let volumeGenerator = 
        Gen.choose (1, 16)
        |> Gen.map(fun x -> (decimal x / 8.0M) * 1.0M<ContainerLibrary.oz>)

let commonGenerator =
    Gen.map2 (fun a b -> { Volume = a 
                           Weight = b})
                     (volumeGenerator) 
                     (Arb.generate<decimal>)

//in case you like applicative style, otherwise completely equivalent
let commonGeneratorAlternative =
    (fun a b -> { Volume = a; Weight = b}) <!> volumeGenerator <*> Arb.generate<decimal>

let bowlGenerator = Gen.map Bowl commonGenerator
let cupGenerator = Gen.map Cup commonGenerator

let containerGenerator =
    Gen.oneof [bowlGenerator; cupGenerator]

type Generators =
    static member Container() = containerGenerator |> Arb.fromGen

module Tests =
open FsCheck.Xunit
open ContainerLibrary

//use PropertyAttribute from FsCheck.Xunit
//use the defined container generator - can also move this to module level
//other ways to parametrize
[<Property(Arbitrary=[|typeof<DataGen.Generators>|])>]
//thanks to PropertyAttribute can now just take container as argument
let ``01 : Containers must be no more than 20 oz`` (container:Container) =
    match container with
    | Cup common
    | Bowl common -> common.Volume <= 20.0M<oz>
    |> Prop.collect container //see the generated values in the output

これは次のようなものを出力します:

よし、100回のテストに合格した。

1% カップ {ボリューム = 2.0M; 重量 = -0.0000221360928858744815609M;}. 1% カップ {ボリューム = 1.8750M; 重量 = 922337.20325598085121M;}. 等

于 2015-01-06T09:23:34.980 に答える