🧪 Table-Driven Tests: Хватит плодить функции-клоны
Проверяю PR джуниора. Файл на 500 строк, из них 450 - это тесты. Смотрю внимательнее, а там Test_ValidateEmail_Empty, Test_ValidateEmail_NoAt, Test_ValidateEmail_Valid... Каждая функция копирует предыдущую на 90%. Любое изменение сигнатуры ValidateEmail заставит переписывать весь этот ад руками.
Коллеги, в Go так не делают. Наш путь - Table-Driven Tests.
Идея гениально проста: мы отделяем данные от логики тестирования. Логика пишется один раз, а тест-кейсы складываются в компактную таблицу (обычно это слайс анонимных структур).
Как это выглядит на практике:
func TestDivide(t *testing. T) {
// 1. Наша "таблица" тест-кейсов
tests := []struct {
name string // Имя кейса (обязательно)
a, b float64 // Входящие аргументы
want float64 // Ожидаемый результат
wantErr bool // Ожидаем ли ошибку?
}{
{"normal division", 10, 2, 5, false},
{"divide by zero", 10, 0, 0, true},
{"fractional result", 5, 2, 2.5, false},
}
// 2. Единая логика прогона
for _, tt := range tests {
t. Run(tt.name, func(t *testing. T) {
got, err := Divide(tt.a, tt.b)
if (err != nil) != tt.wantErr {
t. Fatalf("Divide() error = %v, wantErr %v", err, tt.wantErr)
}
if got != tt.want {
t. Errorf("Divide() got = %v, want %v", got, tt.want)
}
})
}
}
1. DRY (Don't Repeat Yourself). Логика вызова и проверок написана один раз.
2. Читаемость. Таблица читается как документация к функции. Сразу видно все граничные условия.
3. Легкость расширения. Добавить новый кейс - это добавить одну строчку в структуру, а не писать новую функцию на 15 строк.
🔥 Senior Tips:
• Используйте Map вместо Slice. Замените []struct{...} на map[string]struct{...}, где ключ мапы - это name теста. Почему? Итерация по мапам в Go рандомизирована. Это значит, что при каждом запуске ваши тест-кейсы будут выполняться в разном порядке. Это отличный бесплатный способ убедиться, что тесты не зависят друг от друга (не шарят глобальное состояние)!
• t. Parallel() для скорости.
Если в цикле выполняются тяжелые тесты (например, интеграционные с БД), добавьте t. Parallel() первой строкой внутри t. Run. (Только помните про замыкание переменных цикла, если сидите на Go < 1.22!).
Пишите тесты так, чтобы их хотелось читать, а не прокручивать с закрытыми глазами.
#golang #testing #cleancode #bestpractices
📲 https://max.ru/xakkep_1 https://max.ru/golang_lib
👉