Fuzzing в Go — это не рандомные входные данные, а направленный поиск

Кандидаты часто путают fuzzing с генерацией случайных тестовых данных. Это неточно: нативный fuzzing в Go (с версии 1.18) — не слепой перебор, а coverage-guided поиск. Движок отслеживает, какие ветки кода выполнились на каждом входе, и мутирует именно те данные, что открыли новый путь в коде.

Функция объявляется как FuzzXxx(f *testing.F). Внутри — seed corpus: несколько стартовых значений через f.Add(...), и сама логика фаззинга через f.Fuzz(func(t *testing.T, ...) {...}).

func FuzzParse(f *testing.F) { f.Add("2024-01-15") f.Add("") f.Fuzz(func(t *testing.T, s string) { _, err := ParseDate(s) if err != nil { return } // инвариант: успешный // парсинг не паникует дальше }) }

Запускается отдельно: go test -fuzz=FuzzParse. Обычный go test прогоняет только seed corpus, без собственно фаззинга — это важно для CI, иначе сборка будет висеть часами.

На собеседовании почти всегда следом спрашивают: когда fuzzing оправдан, а когда это трата времени. Ответ — там, где входные данные приходят извне и их формат сложный: парсеры, десериализация, работа со строками переменной длины, бинарные протоколы. На CRUD-хендлере с валидацией через структуру это избыточно — там достаточно table-driven тестов на граничные случаи.

Найденные крэши fuzzing сохраняет в testdata/fuzz — это обычные регрессионные тесты, которые остаются в репозитории навсегда.

Тренажёр: 1900+ вопросов и задач, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки