Ответ
Тестирование — это встроенная и первоклассная возможность языка Go, для которой используется стандартный пакет testing и утилита go test.
Основные правила:
- Тесты находятся в файлах с суффиксом
_test.go. - Тестовые файлы располагаются в том же пакете, что и тестируемый код.
- Тестовая функция должна начинаться с
Test, принимать один аргументt *testing.Tи не возвращать значений.
1. Простой юнит-тест:
// В файле sum.go
func Sum(a, b int) int {
return a + b
}
// В файле sum_test.go
func TestSum(t *testing.T) {
got := Sum(2, 3)
want := 5
if got != want {
t.Errorf("Sum(2, 3) = %d; want %d", got, want)
}
}
2. Табличные тесты (Table-Driven Tests):
Это идиоматичный и предпочтительный способ для проверки множества кейсов. Для изоляции тестов и более четкого вывода используется t.Run().
func TestSumTable(t *testing.T) {
tests := []struct {
name string // Имя под-теста
a int
b int
want int
}{
{"positive numbers", 1, 2, 3},
{"zeroes", 0, 0, 0},
{"negative and positive", -1, 1, 0},
{"negative numbers", -2, -3, -5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Sum(tt.a, tt.b)
if got != tt.want {
t.Errorf("Sum(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want)
}
})
}
}
3. Бенчмарки (Benchmarks):
Для измерения производительности. Функция должна начинаться с Benchmark и принимать *testing.B.
func BenchmarkSum(b *testing.B) {
// b.N - это количество итераций, которое определяет сам фреймворк
for i := 0; i < b.N; i++ {
Sum(100, 200)
}
}
Запуск тестов:
go test: запуск всех тестов в текущем пакете.go test -v: запуск с подробным выводом, включая имена запущенных тестов.go test ./...: запуск тестов во всех под-пакетах.go test -run TestSumTable/zeroes: запуск конкретного под-теста.go test -cover: показать покрытие кода тестами.go test -bench .: запуск всех бенчмарков.
Ответ 18+ 🔞
А, тестирование в Go! Ну что ж, дружище, сейчас я тебе так разложу эту хуйню по полочкам, что ты сам захочешь тесты писать, даже если обычно ты их как чёрт ладана боишься.
Смотри, в Go тестирование — это не какая-то обуза, которую прикрутили сверху, а прямо в языке зашито, на уровне пизды. Всё через стандартный пакет testing и команду go test. Красота, а не жизнь!
Главные правила, которые надо запомнить, как «отче наш»:
- Файлы с тестами называй так:
сумма_test.go. Видишь_test? Вот это наш маркер, наш маячок. - Кидай эти файлы в тот же самый пакет, где и основной код лежит. Не выёбывайся.
- Сама тестовая функция должна орать на всех углах, что она тест: начинаться с
Test, принимать одну штукуt *testing.Tи нихуя не возвращать. Вообще.
1. Ну, самый простой тест, для разминки:
// В файле sum.go
func Sum(a, b int) int {
return a + b
}
// В файле sum_test.go
func TestSum(t *testing.T) {
got := Sum(2, 3) // Что получили
want := 5 // Чего ждали
if got != want { // А если не сошлось?
t.Errorf("Sum(2, 3) = %d; want %d", got, want) // То орем на всю деревню
}
}
Вот и всё, ёпта! Запустил go test — и сиди, смотри, как зелёные строчки бегут. Или красные, если накосячил.
2. Теперь главная фишка — табличные тесты (Table-Driven Tests). Это, блядь, святое! Все так делают. Вместо того чтобы десять функций городить, ты пишешь одну, но туда запихиваешь овердохуища разных случаев.
func TestSumTable(t *testing.T) {
tests := []struct {
name string // Имя подтеста, чтоб в отчёте понятно было, что сломалось
a int
b int
want int
}{
{"положительные числа", 1, 2, 3},
{"нули", 0, 0, 0},
{"минус и плюс", -1, 1, 0},
{"оба отрицательные", -2, -3, -5},
}
for _, tt := range tests { // Бежим по всем случаям
t.Run(tt.name, func(t *testing.T) { // Запускаем каждый как отдельный подтест
got := Sum(tt.a, tt.b)
if got != tt.want {
t.Errorf("Sum(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want)
}
})
}
}
Вот это уже мощь! t.Run() — это чтобы если один кейс ебнулся, остальные всё равно побежали, и ты видел, где конкретно пиздец, а не гадал на кофейной гуще.
3. Бенчмарки, или «а не тормозит ли моя функция, как говно в проруби?»
Тут функция должна кричать Benchmark и хватать *testing.B.
func BenchmarkSum(b *testing.B) {
// b.N — это магия. Сам фреймворк решает, сколько раз прогнать цикл, чтобы замер был точным.
for i := 0; i < b.N; i++ {
Sum(100, 200) // Гоняем туда-сюда
}
}
Как это всё запускать, чтобы не опозориться:
go test— всё в текущем пакете пошло-поехало.go test -v— подробненько так, с именами тестов. Люблю это дело.go test ./...— война на всех фронтах, по всем подпакетам.go test -run TestSumTable/нули— запустить только тест для нулей. Удобно, когда дебажишь.go test -cover— посмотреть, какой процент кода ты своими тестами прикрыл. Стыдно, если мало.go test -bench .— погнали бенчмарки! Смотрим, не сосёт ли наша функция.
Вот и вся наука. Не так страшен чёрт, как его малюют. Главное — начать, а там, глядишь, и понравится, когда после рефакторинга все тесты зелёные и ты спокоен, как удав.