Не удается запустить тест на основе модели

В качестве упражнения я хотел реализовать дерево из 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>>

и измените остальные соответственно.

Другие вопросы по тегам