Какой у вас опыт в юнит-тестировании на Go?

Ответ

Да, я активно писал юнит-тесты в Go, так как считаю их неотъемлемой частью разработки. В своей работе я использовал:

  • Стандартную библиотеку testing: для создания базовых тестов, бенчмарков (testing.B) и запуска примеров (testing.E).
  • testify/assert и testify/require: для более читаемых и удобных проверок. assert продолжает выполнение теста после ошибки, а require — прерывает.
  • Табличные тесты (Table-Driven Tests): это идиоматический подход в Go для проверки множества кейсов одним тестом. Он делает код чище и упрощает добавление новых сценариев.

Пример табличного теста:

func TestAdd(t *testing.T) {
    testCases := []struct {
        name string
        a, b int
        want int
    }{
        {"Positive numbers", 2, 3, 5},
        {"Negative numbers", -2, -3, -5},
        {"With zero", 5, 0, 5},
    }

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

Принципы, которых я придерживаюсь:

  1. Изоляция: Тесты должны быть независимы друг от друга.
  2. Мокирование: Для изоляции от внешних зависимостей (базы данных, API) я использую моки. Создаю их либо вручную через интерфейсы, либо с помощью инструментов вроде gomock или testify/mock.
  3. Покрытие: Стараюсь покрывать не только "happy path", но и граничные случаи (edge cases) и ошибки.
  4. CI/CD: Встраиваю запуск тестов (go test ./...) и проверку покрытия (go test -cover) в пайплайны CI/CD для автоматизации.