Разбор реальных багов, найденных с помощью hypothesis
«Баги, которые Hypothesis нашёл в реальных проектах — учитесь на чужих ошибках.»
Команда разрабатывала систему аналитики с сортировкой данных по различным метрикам.
def sort_by_metric(data: list[dict], metric: str) -> list[dict]:
return sorted(data, key=lambda x: x[metric])
# Тест
@given(st.lists(st.builds(dict, value=st.floats())))
def test_sort_preserves_elements(data):
result = sort_by_metric(data, 'value')
assert len(result) == len(data)Falsifying example: data=[{'value': 0}, {'value': inf}, {'value': -inf}]
AssertionError: sorted список не сохраняет порядок при сравнении
Причина: сравнение с inf и -inf работало корректно, но при наличии nan сортировка давала непредсказуемый результат.
from math import isfinite
def sort_by_metric_safe(data: list[dict], metric: str) -> list[dict]:
def safe_key(x):
value = x[metric]
if not isfinite(value):
return float('inf') if value > 0 else float('-inf')
return value
return sorted(data, key=safe_key)Урок: Специальные float значения (inf, -inf, nan) должны явно обрабатываться.
E-commerce платформа с системой скидок.
def calculate_discount(price: float, quantity: int, discount_percent: float) -> float:
total = price * quantity
discount = total * (discount_percent / 100)
return total - discountFalsifying example: price=1e+308, quantity=10, discount_percent=0.0
OverflowError: result too large
Причина: 1e+308 * 10 превышает максимальное float значение.
from decimal import Decimal
def calculate_discount_safe(price: Decimal, quantity: int, discount_percent: Decimal) -> Decimal:
total = price * quantity
discount = total * (discount_percent / 100)
return total - discountУрок: Для финансовых вычислений используйте Decimal, не float.
Система бронирования с проверкой доступности дат.
from datetime import datetime
def is_available(booking_start: datetime, booking_end: datetime, check_date: datetime) -> bool:
return not (booking_start <= check_date <= booking_end)Falsifying example:
booking_start=datetime(2023, 1, 1, tzinfo=UTC),
booking_end=datetime(2023, 12, 31, tzinfo=UTC),
check_date=datetime(2023, 6, 15) # naive datetime
TypeError: can't compare offset-naive and offset-aware datetimes
Причина: Смешение timezone-aware и naive datetime.
def is_available_safe(booking_start, booking_end, check_date):
# Нормализация к одному формату
def ensure_utc(dt):
if dt.tzinfo is None:
return dt.replace(tzinfo=timezone.utc)
return dt.astimezone(timezone.utc)
start = ensure_utc(booking_start)
end = ensure_utc(booking_end)
check = ensure_utc(check_date)
return not (start <= check <= end)Урок: Всегда используйте timezone-aware datetime или явно обрабатывайте naive.
Валидация email при регистрации пользователей.
import re
def is_valid_email(email: str) -> bool:
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))Falsifying example: email='пользователь@пример.рф'
AssertionError: Валидный email отклонён
Причина: Regex не поддерживает Unicode домены и локальные части.
import re
def is_valid_email_unicode(email: str) -> bool:
# Более сложный паттерн с Unicode поддержкой
pattern = r'^[\w.%+-]+@[\w.-]+\.[\w]{2,}$'
return bool(re.match(pattern, email, re.UNICODE))Урок: Учитывайте Unicode при валидации пользовательских данных.
Система управления очередью задач с конкурентным доступом.
class TaskQueue:
def __init__(self):
self.queue = []
self.processing = None
def start_task(self):
if self.queue and self.processing is None:
self.processing = self.queue.pop(0)
def complete_task(self):
if self.processing:
self.processing = NoneFalsifying example:
state = TaskQueueMachine()
state.add_task()
state.add_task()
state.start_task()
state.start_task() # Второй вызов когда processing уже установлен!
AssertionError: processing должен быть None перед start_task
Причина: Отсутствие проверки в start_task что processing уже установлен.
def start_task_safe(self):
if self.queue and self.processing is None:
self.processing = self.queue.pop(0)
# Явно игнорируем вызов если processing уже естьУрок: Stateful testing находит race conditions и ошибки состояния.
Эти кейсы показывают что Hypothesis находит реальные баги в production коде:
Используйте Hypothesis для предотвращения подобных багов в ваших проектах.
Курс завершён! Вы изучили все аспекты property-based тестирования с Hypothesis.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.