TGViewer
Библиотека Go-разработчика | Golang Библиотека Go-разработчика | Golang @goproglib · 24.1K subscribers
Post #7217 3.3K
👨‍💻 Бест-практисы юнит-тестов в Go

Пакет testing в Go покрывает большинство задач без сторонних библиотек. Но сам по себе инструмент не гарантирует качество тестов. Ниже собраны практики, которые помогают писать тесты понятнее, надёжнее и проще в поддержке.

1. Пишите table-driven тесты

Это идиоматический подход в Go. Все сценарии описываются в слайсе структур и прогоняются в цикле через t.Run. Добавить новый кейс — одна строка. Сразу видно, что покрыто, а что нет:
tests := []struct {
name string
input int
expected int
wantErr bool
}{
{"positive", 5, 25, false},
{"zero", 0, 0, false},
{"negative", -1, 0, true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := Square(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
}
if result != tt.expected {
t.Errorf("got %d, want %d", result, tt.expected)
}
})
}


2. Используйте t.Run для подтестов

Подтесты позволяют запускать конкретный сценарий по имени. Это экономит время при отладке, когда не нужно гонять весь набор:
go test -run TestSquare/negative


3. Помечайте хелперы через t.Helper()

Без t.Helper() стек ошибки укажет на строку внутри хелпера. С ним — на строку вызова в тесте:
func assertEqual(t *testing.T, got, want int) {
t.Helper()
if got != want {
t.Errorf("got %d, want %d", got, want)
}
}


4. Мокайте зависимости через интерфейсы

Определяете интерфейс, в проде подставляете реальную реализацию, в тестах — мок:
type Storage interface {
Get(id int) (*Item, error)
}

type MockStorage struct {
items map[int]*Item
}

func (m *MockStorage) Get(id int) (*Item, error) {
if item, ok := m.items[id]; ok {
return item, nil
}
return nil, errors.New("not found")
}


5. Тестируйте поведение, а не реализацию

Если тест ломается при рефакторинге внутренней логики без изменения внешнего контракта — это плохой тест. Начинайте с покрытия экспортируемых функций. Один тест проверяет одну вещь.

6. Давайте тестам понятные имена

Имя теста должно описывать сценарий и ожидаемый результат. При падении вы должны понять, что сломалось, не открывая код.

7. Запускайте тесты с флагом -race

Детектор гонок ловит проблемы конкурентного доступа, которые почти невозможно найти вручную.
go test -race ./...


8. Не гонитесь за 100% покрытия

Высокий процент не гарантирует отсутствие багов. Покрывайте критическую логику и граничные случаи. Качество тестов важнее количества.

9. Используйте TestMain для дорогого setup

Если подготовка окружения занимает время, вынесите её в TestMain. Она запускается один раз на весь пакет, а не перед каждым тестом:
func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
os.Exit(code)
}


10. Выбирайте инструменты под задачу

Стандартного testing хватает для большинства случаев. testify добавляет удобные ассерты и моки. httptest закрывает тестирование HTTP-хендлеров. testcontainers-go помогает с интеграционными тестами через Docker. Не тащите лишнего.

➡️ Нашу рассылку тестировать не нужно, её нужно читать

📍 Навигация: Вакансии • Задачи • Собесы

🐸 Библиотека Go-разработчика

#GoToProduction
  • ❤ 12
  • 👍 11
More from @goproglib
  1. Sep 30, 2026🧑‍💻 Эмулятор AWS-сервисов Kumo — это небольшой инструмент на Go для локальной имитации A…
  2. Sep 29, 2026👨‍💻 Библиотека для написания LSP-серверов Написать свой Language Server с нуля на Go сло…
  3. Sep 28, 2026🤔 Вопрос с собеседования по Go Что выведет программа? ❤️ — 1 true / 0 false 🔥 — 1 true /…
  4. Sep 28, 2026👩‍💻 Что на самом деле происходит внутри Go map? После Go 1.24 обычный map внутри работае…
  5. Sep 26, 2026🔥 В Go 1.27 появился portable SIMD До этого SIMD-оптимизации в Go требовали архитектурног…
  6. Sep 25, 2026🤡🤡 📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека Go-разработчика #GoGiggle
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →