Als Übung wollte ich einen 2-3 Finger Baum implementieren. Das sollte die perfekte Gelegenheit sein, das modellbasierte Testen von FsCheck auszuprobieren. Ich entschied mich, das neuere experimental version zu versuchen.Kann nicht modellbasiert arbeiten Test
Bis jetzt habe ich nur einen Befehl für die Testmaschine programmiert, weil ich es bereits schaffe, diese Arbeit zu machen - andererseits hält sie den Beitrag kurz. Der vollständige Code ist unter GitHub verfügbar.
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"
Was ich verstehe, ist dies: Operation<_>.Run
zweimal ausgeführt wird, ein ResizeArray
von einem mit einem einzigen Element aufzubauen. Dann wird Operation<_>.Check
zweimal mit den gleichen Nummern ausgeführt, um in ein einzelnes Element FingerTree<_>
einzufügen.
Der erste der beiden Durchgänge. Single-Element-Tree-Eingang, Hinzufügen macht es zu einem (korrekten) Zwei-Elemente-Baum, der nach dem ersten Befehl gut mit dem Modell verglichen werden kann.
Der zweite Befehl ist immer der eine Fehler. Check
wird mit dem größeren ResizeList
(jetzt 3 Elemente) aber demselben Baum mit einem einzelnen Element wie im ersten Befehl aufgerufen. Das Hinzufügen eines weiteren Elements bringt es natürlich nicht auf Größe 3 und der Test schlägt fehl.
Ich hätte erwartet, dass ich das aktualisierte Modell von Check
für die kommenden Befehle zurückgeben muss. Aber Sie müssen eine Property
zurückgeben, so dass das nicht möglich ist.
Habe ich völlig falsch verstanden, wie ich das angehen soll? Wie sollte ein arbeitsmodellbasierter Test geschrieben werden?
Gut zu wissen! Wird das irgendwo in den Dokumenten erwähnt? Vielleicht sollte es prominenter sein. Vielen Dank. – primfaktor
Deutlich nicht prominent genug, wenn Sie es verpasst haben :) –