В Части 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