Не удается запустить тест на основе модели
В качестве упражнения я хотел реализовать дерево из 2-3 пальцев. Это должно быть прекрасной возможностью попробовать тестирование FsCheck на основе моделей. Я решил попробовать более новую экспериментальную версию.
До сих пор я тестировал только одну команду для тестовой машины, потому что я уже не справлялся с этой работой - с другой стороны, она оставляет сообщение коротким. Полный код доступен на 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"
Что я понимаю, это: Operation<_>.Run
запускается дважды, чтобы создать ResizeArray
от одного с одним элементом. затем Operation<_>.Check
запускается дважды с одинаковыми номерами для вставки в один элемент FingerTree<_>
,
Первый из двух проходов. Входящее одноэлементное дерево, добавление делает его (правильным) двухэлементным деревом, которое хорошо сравнивается с моделью после первой команды.
Вторая команда всегда одна неудачная. Check
называется с большим ResizeList
(теперь 3 элемента), но то же дерево с одним элементом, что и в первой команде. Конечно, добавление еще одного элемента не приводит к размеру 3, и тест не пройден.
Я бы ожидал, что мне нужно вернуть обновленную модель из Check
для команд прийти. Но вам нужно вернуть Property
так что это невозможно.
Я совершенно не понял, как подойти к этому? Как написать рабочий тест на основе модели?
1 ответ
Модельное тестирование предполагает, что "тестируемая система" модифицируется как побочный эффект, когда Check
вызывается для конкретной операции и инициализируется для этого тестового прогона, когда Setup.Actual()
называется. Он предназначен для работы с системами, которые могут изменяться - например, с изменяемым объектом - и этот стиль, хотя и несколько сбивающий с толку, прекрасно работает с такими системами.
Так как тип вашего пальца неизменен, мой совет будет переопределить SutType
чтобы:
type SutType = Ref<FingerTree<TestType>>
и измените остальные соответственно.