🤩 Фаззинг находит то, что вы не догадались проверить — 28 июля 2026 г. в 10:31:00.309
🤩 Фаззинг находит то, что вы не догадались проверить У обычных тестов есть слепое пятно. Они проверяют случаи, которые вы придумали, а баги живут в тех, что не придумали. Покрытие в 90% говорит, сколько строк вы прогнали, но молчит о том, сколько форм входа упустили. Зелёный прогон означает лишь, что код отработал на ваших примерах, а не что устоит против всех. ➡️ Идея в одном сдвиге Вы пишете не пример, а свойство, которое обязано держаться всегда. Машина сама ищет вход, который его ломает. Вместо «на входе X жду Y» вы описываете инвариант: func FuzzParse(f *testing.F) { f.Add("platform=5000") // сиды это ваши готовые кейсы f.Fuzz(func(t *testing.T, rule string) { team, limit, err := ParseBudgetRule(rule) if err != nil { return // ошибка на кривом входе это норма } // раз ошибки нет, результат обязан быть валидным if limit <= 0 || team == "" { t.Errorf("bad result without error for %q", rule) } }) } Частое заблуждение, что фаззер сыплет случайные байты. Современные фаззеры coverage‑guided. Стартуют с ваших сидов и мутируют их, отслеживая, какие мутации открывают новые ветки. Попал в новый путь, копает оттуда. Поэтому к багам он сходится в разы быстрее слепого рандома. Юнит‑тесты проверяют дороги, которые вы построили, фаззер ищет те, о которых вы не знали. ❓ Что ловит Класс ошибок на стыке кода и реальности. Паника на входе, которого «не бывает». parts := strings.SplitN(rule, "=", 2) limit, _ := strconv.Atoi(parts[1]) // "platform" без = и parts[1] не существует // panic: index out of range [1] with length 1 Нарушения инвариантов, когда функция вернула мусор без ошибки, что хуже явной паники. Расхождения round‑trip, когда decode(encode(x)) не равно x. Их объединяет одно. Все появляются, когда снаружи приходит то, чего код не ждал. Обычные тесты это скипают, потому что их пишут под ожидаемые сценарии, а вы рассуждаете как автор кода, который знает, как им пользоваться. Фаззеру эта рамка незнакома. Найденный падающий вход не исчезает. Go сам кладёт минимальный контрпример в testdata/fuzz/ и делает из него регрессию: go test -fuzz=FuzzParse -fuzztime=30s # ищем баг go test ./... # корпус гоняется всегда, даже без -fuzz Один раз пойманный баг вернуться уже не может. ➡️ Куда прикладывать Туда, где код разбирает или валидирует внешний вход. Парсеры, декодеры, заголовки, query, конфиги, всё, что ветвится по содержимому. Чем больше условной логики, тем больше путей фаззеру. Не стоит фаззить чистую бизнес‑логику без зависимости от входа и функции, которые ходят в сеть или базу, их фаззер дёрнет тысячи раз. Правило короткое. Зависит поведение от того, что прислал внешний мир, заведите фаззинг. ❓ Почему окупается Барьер почти нулевой. В Go фаззер встроен в тулчейн с версии 1.18, свойство пишется в считаные строки. Цена это пара минут на разработке. Альтернатива это ждать, пока вход за вас подберёт прод. Боевыми данными, под нагрузкой, в три часа ночи, в виде алерта. Прод пофаззит ваш код в любом случае, вопрос лишь в том, узнаете вы о баге от теста или от дежурного.

