Best practices, типичные ошибки, оптимизация тестов
«Избегайте типичных ошибок и используйте best practices для эффективного PBT.»
# ❌ ПЛОХО: Слишком много примеров, тесты медленные
@settings(max_examples=10000)
@given(st.integers())
def test_too_slow(x):
...
# ✅ ХОРОШО: Разумный баланс
@settings(max_examples=100)
@given(st.integers())
def test_fast(x):
...
# ✅ Для критичных тестов
@settings(max_examples=500)
@given(st.integers())
def test_critical(x):
...# ❌ ПЛОХО: 99% данных отбрасывается
@given(st.integers().filter(lambda x: x > 1000000))
def test_strict_filter(x):
...
# ✅ ХОРОШО: Используйте assume() или правильные границы
@given(st.integers(min_value=1000001, max_value=2000000))
def test_correct_range(x):
...
# ✅ Или assume() для сложной логики
@given(st.integers(), st.integers())
def test_division(x, y):
assume(y != 0)
assume(x > 0)
...# ❌ ПЛОХО: Бессмысленный инвариант
@given(st.lists(st.integers()))
def test_trivial(lst):
assert lst == lst # Всегда истинно
# ✅ ХОРОШО: Проверяйте свойства
@given(st.lists(st.integers()))
def test_sort_idempotent(lst):
assert sorted(sorted(lst)) == sorted(lst)
# ✅ Проверяйте инварианты предметной области
@given(order_strategy())
def test_order_total(order):
expected = sum(item.price * item.quantity for item in order.items)
assert order.total == expected * (1 - order.discount)# ✅ Добавьте failing case как пример
@example(lst=[0, float('inf'), -float('inf')])
@given(st.lists(st.floats()))
def test_sort(lst):
assert sorted(lst) == sorted(lst)# ❌ ПЛОХО: Это unit test, не PBT
@given(st.integers())
def test_not_property_based(x):
result = some_function(x)
assert result == expected_value(x) # Конкретное значение# ❌ ПЛОХО: Нечитаемая стратегия
@given(st.one_of(
st.builds(dict, a=st.integers()).flatmap(...).filter(...).map(...)
))
def test_complex(data):
...
# ✅ ХОРОШО: Вынесите в отдельную стратегию
@st.composite
def my_complex_strategy(draw):
data = draw(st.builds(dict, a=st.integers()))
# Логика обработки
return data
@given(my_complex_strategy())
def test_readable(data):
...# ❌ ПЛОХО: map скрывает исходные значения
@given(st.integers().map(lambda x: x * 1000000))
def test_bad_shrinking(x):
# Failing case будет большим даже после shrinking
# ✅ ХОРОШО: Используйте правильные границы
@given(st.integers(min_value=0, max_value=1000000))
def test_good_shrinking(x):
...# ✅ Создавайте стратегии один раз
USER_STRATEGY = st.builds(User, ...)
@given(USER_STRATEGY)
def test_cached(user):
...# ❌ ПЛОХО: Медленная операция в тесте
@given(st.lists(st.integers()))
def test_slow(lst):
time.sleep(0.1) # Замедляет все 100 примеров
...
# ✅ ХОРОШО: Вынесите в fixture# ✅ Один тест с несколькими проверками
@given(st.lists(st.integers()))
def test_sort_properties(lst):
result = sorted(lst)
assert len(result) == len(lst)
assert all(result[i] <= result[i+1] for i in range(len(result)-1))
assert Counter(result) == Counter(lst)Следование best practices и избегание антипаттернов делает PBT эффективнее и быстрее.
Следующая тема: Реальные кейсы — разбор багов из production.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.