Ответ
На проекте мы выстроили многоуровневую стратегию тестирования для обеспечения качества и надежности нашего кода:
-
Unit-тесты (Модульные тесты). Мы использовали стандартный пакет
testing
для написания тестов, покрывающих отдельные функции и методы. Основной упор делался на табличные тесты (table-driven tests), что позволяло легко проверять множество граничных случаев.func TestAdd(t *testing.T) { testCases := []struct { name string a, b int want int }{ {"positive numbers", 1, 2, 3}, {"zeroes", 0, 0, 0}, {"negative and positive", -1, 1, 0}, } 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) } }) } }
-
Интеграционные тесты. Проверяли взаимодействие нескольких компонентов системы. Например, связку "сервис-репозиторий-база данных". Для изоляции от внешних зависимостей (БД, сторонние API) мы активно использовали моки, в основном с помощью библиотеки
testify/mock
. -
E2E-тесты (End-to-End). Эти тесты эмулировали полные пользовательские сценарии, отправляя реальные HTTP-запросы к нашему API и проверяя ответы. Для этого использовались как тесты, написанные на Go с помощью стандартного HTTP-клиента, так и инструменты вроде Postman/Newman для автоматизации коллекций.
-
CI/CD. Все тесты автоматически запускались в нашем CI/CD-пайплайне (например, GitHub Actions) при каждом коммите в pull request. Сборка не могла быть влита в основную ветку, если тесты проваливались.
Мы также следили за метрикой покрытия кода тестами (go test -cover
), стремясь поддерживать её на уровне не ниже 80% для критически важных пакетов.