Как в Go пишут unit тесты со стандартным пакетом testing? Какие есть библиотеки, например, testify?go-67

1. Стандартный пакет testing

Go предоставляет встроенный пакет testing для написания тестов. Вот как им пользоваться:

1.1. Базовый пример теста

// math.go
package math

func Add(a, b int) int {
    return a + b
}
// math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    if result != 5 {
        t.Errorf("Add(2, 3) = %d; want 5", result)
    }
}

1.2. Табличные тесты

Идеальный подход для большинства случаев:

func TestAdd_TDD(t *testing.T) {
    tests := []struct {
        name string
        a, b int
        want int
    }{
        {"positive", 2, 3, 5},
        {"negative", -1, -1, -2},
        {"zero", 0, 0, 0},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := Add(tt.a, tt.b); got != tt.want {
                t.Errorf("Add() = %v, want %v", got, tt.want)
            }
        })
    }
}

1.3. Полезные методы testing.T

  • t.Error() / t.Errorf() - сообщение об ошибке без остановки теста
  • t.Fatal() / t.Fatalf() - критическая ошибка, прерывающая тест
  • t.Log() - вывод дополнительной информации
  • t.Helper() - пометка функции как вспомогательной
  • t.Parallel() - запуск теста параллельно

2. Популярные библиотеки для тестирования

2.1. Testify

Наиболее популярный набор инструментов:

import (
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
    "github.com/stretchr/testify/suite"
)

// Простые assertions
func TestAdd_Testify(t *testing.T) {
    result := Add(2, 3)
    assert.Equal(t, 5, result, "they should be equal")
    require.NotNil(t, result, "result should not be nil")
}

// Test suites
type MathTestSuite struct {
    suite.Suite
}

func (s *MathTestSuite) TestAdd() {
    s.Equal(5, Add(2, 3))
}

func TestMathSuite(t *testing.T) {
    suite.Run(t, new(MathTestSuite))
}

Преимущества Testify:

  • Богатый набор assertions
  • Поддержка test suites
  • Удобные сообщения об ошибках
  • Mock-функциональность (в пакете mock)

2.2. Gomock

Генерация моков на основе интерфейсов:

  1. Установка:
go install github.com/golang/mock/mockgen@latest
  1. Генерация моков:
mockgen -source=repository.go -destination=repository_mock.go -package=mocks
  1. Использование в тестах:
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockRepo := mocks.NewMockRepository(ctrl)
mockRepo.EXPECT().
    FindByID(123).
    Return(&User{Name: "John"}, nil)

2.3. Другие полезные библиотеки

  • httptest - стандартный пакет для тестирования HTTP
  • sqlmock - для мокирования SQL-запросов
  • testcontainers-go - для интеграционных тестов с Docker
  • ginkgo/gomega - BDD-стиль тестирования

3. Практические советы

  1. Именование:

    • Файлы: *_test.go
    • Функции: Test[Функция]_[Сценарий] или Test[Функция]
    • Переменные: tt (test case), want (ожидаемое значение)
  2. Организация:

    • Тесты рядом с тестируемым кодом
    • internal/testutils для вспомогательных функций
    • Отдельные пакеты для интеграционных тестов
  3. Покрытие:

    • Проверка: go test -cover
    • Визуализация: go test -coverprofile=coverage.out && go tool cover -html=coverage.out
  4. Параллелизация:

func TestParallel(t *testing.T) {
    t.Parallel()
    // тестовый код
}

Резюмируем

Go предлагает:

  1. Мощный стандартный пакет testing для unit-тестов
  2. Богатую экосистему библиотек (Testify, Gomock и др.)
  3. Гибкие подходы к организации тестов (TDD, suites)
  4. Инструменты для измерения покрытия и параллельного выполнения

Выбор инструментов зависит от проекта:

  • Для простых случаев достаточно стандартного пакета
  • Testify удобен для сложных assertions
  • Gomock незаменим для работы с интерфейсами