Когда при реализации тестирования на основе свойств следует использовать генератор входных данных вместо выражения предварительного условия?

Когда при реализации тестирования на основе свойств следует использовать генератор входных данных вместо выражения предварительного условия?

Существуют ли соображения производительности при выборе конкретного варианта?

Внутри один метод неизбежно использует другой?

Я бы подумал, что выражение предварительного условия будет выполняться дольше, чем генератор ввода. Кто-нибудь проверял это?

Зачем нам оба?


person Scott Nimrod    schedule 24.03.2016    source источник
comment
Вы должны хотя бы высказать то, что считаете правильным, и объяснить. Тогда другие узнают, думают ли они так же, и смогут увидеть или дать другой ответ.   -  person Guy Coder    schedule 24.03.2016
comment
@Гай Кодер. Я уточнил некоторые. Спасибо за отзыв.   -  person Scott Nimrod    schedule 24.03.2016
comment
При запуске теста моей первой заботой является охват всех возможных условий, а не то, сколько времени они занимают. Хотя я не часто использовал стандартные генераторы тестов, когда я это делаю, я склонен обнаруживать, что они дают вам ложное представление о том, что код правильный, хотя на самом деле есть условие, которое они пропустили. Лично я предпочитаю использовать оба, когда это возможно, поскольку иногда один улавливает что-то, что пропустил другой. Другими словами, я иногда пропускаю случай, когда генератор найдет.   -  person Guy Coder    schedule 24.03.2016
comment
Что не так с инструментами по умолчанию? Или это обучающее упражнение?   -  person Scott Nimrod    schedule 24.03.2016
comment
В частности, с парсерами комбинаторный взрыв велик. Однажды я сделал расчет теста, который я хотел бы создать для C++, и количество тестов оказалось равным 10**27, то есть 10 с 27 нулями. Я также стараюсь не создавать тест на основе просмотра кода, потому что он подстраивается под тестируемый код, но когда вы пишете код и тест, то и то, и другое не слишком сложно. Не поймите меня неправильно, я предпочитаю тестировать с помощью генераторов, чем без них, но иногда правила для них хуже, чем код, который они тестируют.   -  person Guy Coder    schedule 24.03.2016
comment
Я немного понимаю. Удачи в этом. знак равно   -  person Scott Nimrod    schedule 24.03.2016
comment
Я только что купил новую книгу F # 4.0 час назад. Я продвинулся. Но я все еще не запрограммирован еще.   -  person Scott Nimrod    schedule 24.03.2016


Ответы (1)


Когда вы используете предварительное условие (например, оператор ==> в FsCheck), вы, по сути, выбрасываете данные. Даже если это произойдет только в одном из ста случаев, вы все равно выбросите 1 входной набор для обычного свойства (поскольку количество выполнений по умолчанию равно 100 в FsCheck).

Выбросить один из 100, наверное, не проблема.

Однако иногда вы выбрасываете намного больше данных. Если, например, вам нужны только положительные числа, вы можете написать предварительное условие, такое как x > 0, но, поскольку FsCheck генерирует и отрицательные числа, вы выбросите 50 % всех значений после того, как они были сгенерированы. Скорее всего, ваши тесты будут работать медленнее (но, как всегда, когда дело доходит до соображений производительности: измерьте).

Именно по этой причине FsCheck поставляется со встроенными генераторами положительных чисел, но иногда вам требуется более точный контроль диапазона возможных входных значений, как в этот пример.

Если вы делаете ката FizzBuzz, например, вы можете напишите свой тест для случая FizzBuzz следующим образом:

[<Property(MaxFail = 2000)>]
let ``FizzBuzz.transform returns FizzBuzz`` (number : int) =
    number % 15 = 0 ==> lazy
    let actual = FizzBuzz.transform number
    let expected = "FizzBuzz"
    expected = actual

Обратите внимание на использование свойства MaxFail. Причина, по которой вам это нужно, заключается в том, что это предварительное условие отбрасывает 14 из 15 сгенерированных кандидатов. По умолчанию FsCheck попытается проверить 1000 кандидатов, прежде чем сдастся, но если вы отбросите 14 из 15 кандидатов, в среднем у вас будет только 67 значений, соответствующих предварительному условию. Поскольку целью FsCheck по умолчанию является выполнение свойства 100 раз, он сдается.

Как следует из свойства MaxFail, вы можете настроить значения по умолчанию. С 2000 кандидатов вы должны ожидать в среднем 133 совпадения предварительных условий.

Однако он не кажется особенно эффективным, поэтому вы можете в качестве альтернативы использовать собственный генератор:

[<Property(QuietOnSuccess = true)>]
let ``FizzBuzz.transform returns FizzBuzz`` () =
    let fiveAndThrees =
        Arb.generate<int> |> Gen.map ((*) (3 * 5)) |> Arb.fromGen
    Prop.forAll fiveAndThrees <| fun number ->

        let actual = FizzBuzz.transform number

        let expected = "FizzBuzz"
        expected = actual

При этом используется специальный встроенный файл Arbitrary. Это более эффективно, потому что никакие данные не выбрасываются.

Я склонен использовать предварительные условия, если они выдают только исключить случайный несовпадающий ввод. В большинстве случаев я предпочитаю пользовательские генераторы.

person Mark Seemann    schedule 24.03.2016
comment
Индекс модульного тестирования — http://blog.ploeh.dk/tags/#Unit Testing-ref для блогов Марка. По какой-то причине это не позволяет мне сделать это правильной ссылкой в ​​этом комментарии. - person Guy Coder; 24.03.2016