Объясните различия между примерным (example-based) тестированием и тестированием свойств (property-based testing), и приведите задачу, где property-based тесты эффективнее
Кратко — по отличиям и примеру. Отличия - Подход: - Example-based: пишут конкретные входы и ожидаемые выходы (набор тест-кейсов). - Property-based: описывают общие свойства/инварианты функции, а фреймворк генерирует множество случайных входов. - Покрытие: - Example-based хорошо для известных краевых случаев и регрессий. - Property-based ищет неожиданные/редкие кейсы за счёт случайной генерации и сокращения найденного контрпримера (shrinking). - Оракул: - Example-based использует явное ожидаемое значение. - Property-based требует формулировки свойства (инварианта), а не конкретного результата. - Диагностика: - Example-based — сразу понятно, какой вход и ожидаемый выход. - Property-based при провале даёт минимальный контрпример; иногда нужно анализировать, почему свойство не выполняется. - Трудоёмкость: - Example-based проще начать. - Property-based требует продуманной генерации данных и корректного свойства. - Лучше подходит для: - Example-based — конкретные бизнес-правила и регрессии. - Property-based — алгоритмы, структуры данных, сериализация/десериализация, матем. свойства. Пример задачи, где property-based эффективнее - Тестирование сортировки по свойствам (вместо перечисления всех возможных списков): - Свойство 1: выход отсортирован: ∀l: is_sorted(sort(l))\forall l:\ \text{is\_sorted}(\text{sort}(l))∀l:is_sorted(sort(l)). - Свойство 2: выход — перестановка входа (сохраняется мультимножество): ∀l: multiset(sort(l))=multiset(l)\forall l:\ \text{multiset}(\text{sort}(l)) = \text{multiset}(l)∀l:multiset(sort(l))=multiset(l). Фреймворк сгенерирует короткие и длинные списки, с дубликатами, уже отсортированные, реверсированные, очень большие, краевые случаи — и при нахождении ошибки предоставит минимальный контрпример. Ещё классическая и практическая задача — проверка сериализации/десериализации: для любой структуры данных xxx должно выполняться deserialize(serialize(x))=x\text{deserialize}(\text{serialize}(x)) = xdeserialize(serialize(x))=x. Property-based автоматически подберёт вложенные структуры, пустые значения, специальные символы и т.д., часто находя ошибки, которые ручные примеры пропустят. Когда property-based эффективнее: когда пространство входов велико/структурно сложное и вы можете выразить правильность через свойства/инварианты (алгоритмы, структуры данных, парсеры/сериализаторы, математические функции).
Отличия
- Подход:
- Example-based: пишут конкретные входы и ожидаемые выходы (набор тест-кейсов).
- Property-based: описывают общие свойства/инварианты функции, а фреймворк генерирует множество случайных входов.
- Покрытие:
- Example-based хорошо для известных краевых случаев и регрессий.
- Property-based ищет неожиданные/редкие кейсы за счёт случайной генерации и сокращения найденного контрпримера (shrinking).
- Оракул:
- Example-based использует явное ожидаемое значение.
- Property-based требует формулировки свойства (инварианта), а не конкретного результата.
- Диагностика:
- Example-based — сразу понятно, какой вход и ожидаемый выход.
- Property-based при провале даёт минимальный контрпример; иногда нужно анализировать, почему свойство не выполняется.
- Трудоёмкость:
- Example-based проще начать.
- Property-based требует продуманной генерации данных и корректного свойства.
- Лучше подходит для:
- Example-based — конкретные бизнес-правила и регрессии.
- Property-based — алгоритмы, структуры данных, сериализация/десериализация, матем. свойства.
Пример задачи, где property-based эффективнее
- Тестирование сортировки по свойствам (вместо перечисления всех возможных списков):
- Свойство 1: выход отсортирован: ∀l: is_sorted(sort(l))\forall l:\ \text{is\_sorted}(\text{sort}(l))∀l: is_sorted(sort(l)).
- Свойство 2: выход — перестановка входа (сохраняется мультимножество): ∀l: multiset(sort(l))=multiset(l)\forall l:\ \text{multiset}(\text{sort}(l)) = \text{multiset}(l)∀l: multiset(sort(l))=multiset(l).
Фреймворк сгенерирует короткие и длинные списки, с дубликатами, уже отсортированные, реверсированные, очень большие, краевые случаи — и при нахождении ошибки предоставит минимальный контрпример.
Ещё классическая и практическая задача — проверка сериализации/десериализации: для любой структуры данных xxx должно выполняться deserialize(serialize(x))=x\text{deserialize}(\text{serialize}(x)) = xdeserialize(serialize(x))=x. Property-based автоматически подберёт вложенные структуры, пустые значения, специальные символы и т.д., часто находя ошибки, которые ручные примеры пропустят.
Когда property-based эффективнее: когда пространство входов велико/структурно сложное и вы можете выразить правильность через свойства/инварианты (алгоритмы, структуры данных, парсеры/сериализаторы, математические функции).