1

演習として、2 ~ 3 本のフィンガー ツリーを実装したいと考えました。これは、 FsCheckのモデルベースのテストを試す絶好の機会です。新しい実験版を試すことにしました。

これまでのところ、テスト マシン用のコマンドを 1 つだけコーディングしました。完全なコードはGitHubで入手できます。

open CmdQ
open Fuchu
open FsCheck
open FsCheck.Experimental

type TestType = uint16
type ModelType = ResizeArray<TestType>
type SutType = FingerTree<TestType>

let spec =
    let prepend (what:TestType) =
        { new Operation<SutType, ModelType>() with
            override __.Run model =
                // Also tried returning the same instance.
                let copy = model |> ResizeArray
                copy.Insert(0, what)
                copy

            override __.Check(sut, model) =
                let sutList = sut |> Finger.toList
                let newSut = sut |> Finger.prepend what
                let newSutList = newSut |> Finger.toList
                let modelList = model |> Seq.toList
                let areEqual = newSutList = modelList
                areEqual |@ sprintf "prepend: model = %A, actual = %A (incoming was %A)" modelList newSutList sutList

            override __.ToString() = sprintf "prepend %A" what
        }

    let create (initial:ModelType) =
        { new Setup<SutType, ModelType>() with
            override __.Actual () = initial |> Finger.ofSeq

            override __.Model () = initial //|> ResizeArray // Also tried this.
        }

    let rndNum () : Gen<TestType> = Arb.from<uint16> |> Arb.toGen

    { new Machine<SutType, ModelType>() with
        override __.Setup =
            rndNum()
            |> Gen.listOf
            |> Gen.map ResizeArray
            |> Gen.map create
            |> Arb.fromGen

        override __.Next _ = gen {
            let! cmd = Gen.elements [prepend]
            let! num = rndNum()
            return cmd num
        }
    }

[<Tests>]
let test =
    [spec]
    |> List.map (StateMachine.toProperty >> testProperty "Finger tree")
    |> testList "Model tests"

私が理解しているのは、単一の要素を持つ 1 つから 1 つOperation<_>.Runを構築するために 2 回実行されるということです。ResizeArrayその後Operation<_>.Check、同じ番号で 2 回実行され、1 つの要素に挿入されますFingerTree<_>

2 つのパスの最初のパス。単一要素ツリーを追加すると、最初のコマンドの後のモデルとよく比較される (正しい) 2 要素ツリーになります。

2 番目のコマンドは常に失敗します。Checkより大きなResizeList(現在は 3 つの要素) で呼び出されますが、最初のコマンドと同じ単一要素の Tree です。もちろん、要素をもう 1 つ追加してもサイズ 3 にはならず、テストは失敗します。

Checkコマンドを実行するには、更新されたモデルを から返す必要があると予想していました。ただし、 a を返す必要があるPropertyため、それは不可能です。

これにアプローチする方法を完全に誤解しましたか?動作するモデルベースのテストはどのように作成する必要がありますか?

4

1 に答える 1

2

モデルベースのテストでは、特定の操作で が呼び出されたときに "テスト対象のシステム" が副作用として変更され、Checkが呼び出されたときにそのテスト実行用に初期化されることを前提としていSetup.Actual()ます。これは、変更可能なシステム (変更可能なオブジェクトのようなもの) を扱うことを目的としています。このスタイルは、ここでは多少戸惑いますが、そのようなシステムでは非常にうまく機能します。

あなたの指の木のタイプは不変なので、私のアドバイスは次のように再定義SutTypeすることです:

type SutType = Ref<FingerTree<TestType>>

それに応じて残りを変更します。

于 2016-10-11T14:47:23.917 に答える