Hypothesis – генеративное тестирование

онлайн тренер по питону
Онлайн-тренажер Python 3 для начинающих

Теория без воды. Задачи с автоматической проверкой. Подсказки на русском языке. Работает в любом современном браузере.

начать бесплатно

Введение

Обычные тесты проверяют, что функция работает при заданных значениях. Но что, если вы не знаете заранее, какие входные данные приведут к ошибке? Hypothesis – генеративное тестирование решает эту проблему.

Hypothesis — это библиотека property-based testing для Python. Она автоматически генерирует входные данные, стремясь найти крайние, ошибочные или нестандартные случаи. Вместо ручного подбора аргументов, вы описываете свойства функции, и Hypothesis пытается их нарушить.

Установка и базовый пример

Установка:

bash
pip install hypothesis

Базовый тест:

python
from hypothesis import given import hypothesis.strategies as st @given(st.integers(), st.integers()) def test_addition_commutative(a, b): assert a + b == b + a

Запуск через pytest или python -m unittest.

Основы генерации данных

  • Декоратор @given() указывает, какие стратегии генерации использовать

  • Стратегии находятся в hypothesis.strategies (чаще всего импортируется как st)

Примеры стратегий:

python
@given(st.text()) def test_uppercase(s): assert s.upper().isupper()

Проверка свойств и инвариантов

Hypothesis лучше всего работает, когда вы проверяете общие свойства (invariants), а не конкретные значения.

Например, строка в верхнем регистре должна быть в isupper():

python
@given(st.text()) def test_upper(s): assert s.upper().isupper()

Это позволяет находить баги, которые невозможно покрыть вручную.

Минимизация ошибок (shrinkage)

Если тест упал, Hypothesis:

  • Сохраняет минимальный набор входных данных, вызвавший ошибку

  • Выводит его для повторного использования

Пример:

python
@given(st.integers()) def test_fails_on_zero(x): assert x != 0

Вывод: Falsifying example: test_fails_on_zero(x=0)

Стратегии генерации данных

Некоторые стратегии из hypothesis.strategies:

  • integers()

  • text()

  • lists(elements=...)

  • dictionaries(keys=..., values=...)

  • booleans()

  • dates(), datetimes()

  • emails(), urls()

  • binary(), just(value)

Можно создавать сложные комбинации:

python
st.tuples(st.integers(), st.text())

Или пользовательские:

python
@st.composite def user_data(draw): name = draw(st.text()) age = draw(st.integers(min_value=0, max_value=120)) return {"name": name, "age": age}

Фильтрация и допущения

Если нужно отфильтровать нежелательные значения:

python
@given(st.integers().filter(lambda x: x > 0)) def test_positive(x): assert x > 0

Или использовать assume():

python
from hypothesis import assume @given(st.integers()) def test_only_positive(x): assume(x > 0) assert x > 0

Параметризация и множественные сценарии

Для конкретных примеров используйте @example():

python
from hypothesis import example @given(st.text()) @example(" ") @example("123") def test_strip(s): assert s.strip() == s.strip(" ")

Интеграция с Pytest и UnitTest

С Pytest:

python
@pytest.mark.parametrize(...) @given(...) def test_something(...): ...

С UnitTest:

python
import unittest class TestStrings(unittest.TestCase): @given(st.text()) def test_len(self, s): self.assertLessEqual(len(s), 10000)

Тестирование API и бизнес-логики

Hypothesis может генерировать:

  • JSON-данные

  • Строки запроса

  • Случайные параметры API

Пример генерации словаря:

python
@given(st.dictionaries(st.text(), st.integers())) def test_dict_props(d): assert isinstance(d, dict)

Генерация пользовательских типов и моделей

Можно создавать кастомные стратегии:

python
@st.composite def user_strategy(draw): name = draw(st.text(min_size=1)) age = draw(st.integers(min_value=18)) return {"name": name, "age": age}

Поддерживается генерация моделей Pydantic и объектов SQLAlchemy.

Контроль повторяемости и дебаг

Использование @settings():

python
from hypothesis import settings @settings(max_examples=1000) @given(st.integers()) def test_big_sample(x): ...

Фиксация случайности:

python
from hypothesis import seed @seed(1234) @given(st.integers()) def test_seeded(x): ...

Сравнение с другими инструментами

Инструмент Генерация данных Авто-поиск граничных Поддержка property-based Интеграция с Pytest
Hypothesis Да Да Да Отличная
Pytest Частично Нет Нет Да
Fuzzing (Atheris, AFL) Да Частично Нет Сложнее

Рекомендации и лучшие практики

  • Проверяйте свойства, а не конкретные результаты

  • Используйте assume() и фильтры с осторожностью

  • Избегайте assert x != x — Hypothesis может не найти ошибки в edge-cases

  • Добавляйте @example() для покрытия краевых значений

Часто задаваемые вопросы (FAQ)

1. Что такое Hypothesis?
Это библиотека генеративного тестирования (property-based testing) для Python.

2. Как работает Hypothesis?
Она генерирует входные данные и ищет такие, при которых функция не выполняет заданные условия.

3. Поддерживает ли Hypothesis интеграцию с Pytest?
Да, полностью.

4. Можно ли тестировать API с Hypothesis?
Да, особенно полезно для проверки валидаторов и сериализаторов.

5. Как повторить найденную ошибку?
Hypothesis выводит failing example и сохраняет их в .hypothesis для повторного запуска.

6. Где лучше не использовать Hypothesis?
Когда поведение функции зависит от внешних состояний (время, сеть, глобальные переменные).

Полный справочник по ключевым функциям и модулям библиотеки Hypothesis для Python

Установка

bash
pip install hypothesis

Базовое использование

Декоратор Описание
@given(...) Главный декоратор — указывает, какие данные генерировать.
python
from hypothesis import given from hypothesis.strategies import integers @given(integers()) def test_abs(x): assert abs(x) >= 0

Стратегии (hypothesis.strategies)

Hypothesis использует стратегии для генерации значений разных типов.

Стратегия Описание
integers() Целые числа
floats() Числа с плавающей точкой
booleans() True / False
text() Unicode-строки
emails() Валидные e-mail
dates(), datetimes() Даты и времена
lists(sub_strategy) Списки
dictionaries(keys, values) Словари
tuples(a, b) Кортежи
just(x) Всегда возвращает x
sampled_from([a, b, c]) Случайный элемент из списка
one_of(...) Один из нескольких стратегий

Ограничение диапазонов и фильтрация

Фильтрация Примеры
integers(min_value=1, max_value=10) Числа от 1 до 10
.filter(lambda x: ...) Убирает значения, не проходящие фильтр
.map(func) Трансформация значения
python
@given(integers(min_value=0, max_value=100)) def test_in_range(x): assert 0 <= x <= 100

Комбинирование стратегий

python
from hypothesis.strategies import text, integers, tuples @given(tuples(text(), integers())) def test_combo(data): s, i = data assert isinstance(s, str) assert isinstance(i, int)

Проверка ошибок и assume()

Метод Описание
assume(cond) Прерывает пример, если условие не выполнено (не ошибка).
python
from hypothesis import assume @given(integers()) def test_division(x): assume(x != 0) assert 10 / x != 0

Управление количеством и сложностью тестов

Аргумент Описание
@settings(...) Настройки выполнения: число примеров, таймаут и т.д.
max_examples, deadline, verbosity Параметры @settings
python
from hypothesis import settings @settings(max_examples=50, deadline=None) @given(integers()) def test_custom_settings(x): ...

Использование с pytest

Hypothesis прекрасно работает с pytest "из коробки". Просто запускайте pytest — все @given-тесты будут выполняться.


Использование с unittest

python
import unittest from hypothesis import given from hypothesis.strategies import integers class MyTest(unittest.TestCase): @given(integers()) def test_non_negative(self, x): self.assertTrue(abs(x) >= 0)

Проверка строк

python
from hypothesis.strategies import text @given(text(min_size=1, max_size=10)) def test_nonempty_string(s): assert isinstance(s, str)

Проверка структур и объектов

python
from hypothesis.strategies import builds @dataclass class User: name: str age: int user_strategy = builds(User, name=text(), age=integers(min_value=0)) @given(user_strategy) def test_user(u): assert isinstance(u, User)

Отладка и воспроизведение ошибок

Механизм Описание
При падении Hypothesis выводит минимальный пример, вызывающий ошибку.  
Чтобы воспроизвести ошибку, можно установить переменную:  
HYPOTHESIS_PROFILE=debug и использовать --hypothesis-seed.  
bash
pytest test_file.py --hypothesis-seed=123456

Полезные утилиты

Функция Описание