
Property-based testing на практике: как находить баги, о которых вы не подумали
badcasedaily1 6 минут назад Property-based testing на практике: как находить баги, о которых вы не подумали Средний 8 мин 5 Блог компании OTUS Программирование * Python * Тестирование IT-систем * Тестирование...
Anthropic — What company has the best second artificial intelligence model at the end of June?
Значимый прорыв формирует отрасль ИИ: badcasedaily1 6 минут назад Property-based testing на практике: как находить баги, о которых вы не подумали Средний 8 мин 5 Блог компании OTUS Программирование * Python * Тестирование IT-систем * Тестирование веб-сервисов * Туториал Вы написали тридцать юнит-тестов, подобрали входы руками, всё зелёное, релиз уезжает. Через неделю прод падает на пустой строке, на отрицательном нуле, на числе чуть больше int32, на юникоде с суррогатными парами или на списке с дубликатами. Тест на это не сработал по простой причине: пример-ориентированный тест проверяет ровно те случаи, которые вы придумали, а баги живут в тех, которые не придумали.
Property-based testing может изменить подход. Вместо «на входе X получаю Y» вы формулируете свойство, истинное для любого входа, а фреймворк сам генерирует сотни разнообразных значений и пытается это свойство сломать. В статье разберём на Hypothesis для Python, как писать такие свойства, какие они бывают, как описывать вход, как тестировать системы с состоянием и где этот подход бьёт обычные тесты, а где нет.
Технические детали
Первый property-тестСамое наглядное свойство — round-trip: если данные закодировать, а потом раскодировать, должно получиться исходное. Обычный тест проверил бы пару строк, property-тест проверяет любые. from hypothesis import given, strategies as st @given(st.
text()) def test_roundtrip(s): assert decode(encode(s)) == sHypothesis сгенерирует много значений s, и в их числе те, о которых руками не вспоминают: пустая строка, одиночный перевод строки, эмодзи, символы из разных юникод-плоскостей, суррогатные пары. Если хоть одно сломает равенство, тест упадёт и сообщит конкретное значение. Никакого списка примеров вы не пишете — пишете утверждение, истинное для всего домена.
Зелёный тест и баг, которого он не видитПокажем разницу на конкретной функции. Пусть chunk режет список на куски по n элементов, и в реализации есть распространённая ошибка — теряется последний неполный кусок. def chunk(xs, n): out = for i in range(0, len(xs), n): if i + n <= len(xs): # баг: неполный хвост в результат не попадает out.
append(xs) return outПример-ориентированный тест, написанный на удобных делящихся случаях, проходит и создаёт ложную уверенность:def test_chunk_example(): assert chunk(, 2) == , ] # зелёныйProperty-тест формулирует то, что должно держаться всегда: склейка кусков обратно даёт исходный список, и ни один кусок не длиннее n. integers(min_value=1, max_value=10)) def test_chunk_reassembles(xs, n): chunks = chunk(xs, n) assert == xs # склейка == исходный список assert all(len(c) <= n for c in chunks)Hypothesis быстро находит падение, плюсом минимизирует его до простейшего вида — например, xs=, n=2: функция вернула пустой список, склейка дала , а ожидался . Минимальный контрпример сразу указывает на природу бага — потерю хвоста, а не тонет в случайном списке из сотен элементов.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.





