В Части 1 мы рассмотрели концепцию генераторов. Без них тестирование на основе свойств было бы невозможно. В одиночку они не предлагают много.
В этой статье мы рассмотрим другую основную часть фреймворков для тестирования на основе свойств: бегуны.
В быстрой проверке есть один главный бегун по имени fc.assert
. Давайте посмотрим, как добавить такую функцию в наш минимальный фреймворк.
Та же статья на dev.to по адресу https://dev.to/dubzzz/your-own-property-based-testing-framework-part-2-runners-12fl
Прежде всего, необходимо определить понятие собственности. Для простоты свойство можно рассматривать как супергенератор:
В нашем случае свойства будут создаваться с помощью следующего хелпера:
Теперь давайте рассмотрим простой пример, чтобы понять, как мы хотим использовать нашу минимальную версию быстрой проверки. Тестируемый код будет реализацией isSubstring
с очевидной ошибкой, чтобы проверить, сможет ли наш фреймворк ее найти. Как пользователь, мы хотели бы иметь возможность написать следующий тест:
С точки зрения типов у нас есть следующая подпись, которую нужно выполнить для assert
:
По умолчанию в большинстве фреймворков бегуны запускают свойство сто раз и останавливаются, если после этой сотни запусков все работает нормально.
Базовая реализация бегуна может быть записана следующим образом:
Кроме того, при тестировании на основе свойств предполагается, что начальное значение не фиксируется, за исключением случаев, когда это указано на сайте вызова. Реализация выше может быть обновлена следующим образом:
В предыдущей статье мы не освещали причину, по которой мы выбрали чисто случайные генераторы. В случае, основанном на свойствах, мы хотим, чтобы свойства воспроизводились независимо от начального значения, независимо от оборудования и независимо от времени Unix… Но мы также хотим иметь независимые запуски для каждой итерации в цикле.
Например, в реализации, определенной выше, мы вызываем generate
со следующими экземплярами Random
:
runId = 0
Позвонить с
new Random(prand.xoroshiro128plus(seed))
runId = 1
Позвонить с
new Random(prand.xoroshiro128plus(seed)).jump()
runId = 2
Позвонить с
new Random(prand.xoroshiro128plus(seed)).jump().jump()
- …
jump
смещает генератор случайных чисел. В контексте xoroshiro128plus
вызов jump
эквивалентен 2^64 вызовам next
. В случае чистого ранда ни jump
, ни next
не изменяют исходный экземпляр, они оба создают новый экземпляр генератора, сохраняя исходный без изменений.
Независимо от того, сколько раз наше свойство будет вызывать переданный изменяемый генератор случайных чисел, мы всегда будем игнорировать его, чтобы построить генератор, необходимый для следующей итерации. Хотя на первый взгляд это может показаться странным, эта функция важна, поскольку мы на самом деле не знаем, что произойдет с этим экземпляром нашего генератора случайных чисел. Среди возможных сценариев:
- полагаться на смещение, применяемое свойством к переданному экземпляру
Random
, проблематично, поскольку это затрудняет реализацию повторов, за исключением случаев, когда мы повторно генерируем все значения одно за другим при каждом воспроизведении материала. - Экземпляр
Random
может быть сохранен и повторно использован позже свойством и егоGenerator
(мы увидим, что это может иметь место в некоторых реализациях сжатия), поэтому вызовы generate в последующих итерациях могут изменить его.
Теперь мы можем использовать нашу маленькую структуру для свойства, которое мы обсуждали ранее в этом разделе. Как мы и ожидали, он находит проблему и сообщает о ней. При локальном запуске вы должны получить результат, похожий на:
Учитывая всю вышеперечисленную работу, вы должны иметь возможность писать и тестировать свойства как если бы вы использовали быструю проверку.
Игровой пример доступен по адресу https://runkit.com/dubzzz/part-2-runners.
Следующая часть: https://medium.com/@nicolasdubien/your-own-property-based-testing-framework-part-3-shrinkers-564fa7a180eb