Табличные тесты через пакет testing и go test
Содержание курса
t.Run с литералом функции: запуск каждого случая как именованного подтеста
Таблица готова. Теперь нужно запустить каждый случай и получить отдельный результат для каждого. Именно для этого существует t.Run.
func TestSquare(t *testing.T) {
tests := []struct {
name string
input int
expected int
}{
{"zero", 0, 0},
{"positive", 3, 9},
{"negative", -2, 4},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := Square(tc.input)
if got != tc.expected {
t.Errorf("Square(%d) = %d, want %d", tc.input, got, tc.expected)
}
})
}
}
t.Run принимает два аргумента: строку-имя и функцию с сигнатурой func(t *testing.T). Вместо того чтобы объявлять отдельную именованную функцию где-то в файле, мы пишем литерал функции — анонимную функцию прямо на месте вызова. Разница не в поведении, а в удобстве и области видимости: литерал захватывает переменную tc из окружающего цикла и видит её без явной передачи аргументов.
Чем t.Run принципиально полезнее простого цикла с t.Errorf на внешнем t? Дело не в продолжении выполнения — обычный цикл тоже не останавливается на первом провале. t.Run даёт три конкретных преимущества:
-
Имя в отчёте. Каждый подтест получает собственную строку
--- FAIL: TestSquare/positive, а не безликое сообщение внутри одной большой тест-функции. При большой таблице сразу видно, какие именно случаи не прошли. -
Отдельный статус. Каждый подтест — это самостоятельный
*testing.Tсо своим статусом PASS/FAIL. Провал одного не влияет на итоговый статус других; в отчёте они перечислены независимо. -
Адресный запуск. Любой подтест можно запустить отдельно по имени через флаг
-run:
go test -run TestSquare/positive ./...
Это удобно при отладке: не нужно гонять всю таблицу ради одного случая.
В выводе go test -v каждый подтест виден как TestSquare/zero, TestSquare/positive, TestSquare/negative. Слеш и имя добавляет сам t.Run — именно то значение, которое вы передали первым аргументом.
