Ответ
На проекте мы выстроили многоуровневую стратегию тестирования для обеспечения качества и надежности нашего кода:
-
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% для критически важных пакетов.
Ответ 18+ 🔞
Да ты послушай, как мы тут на проекте качество кодим, блядь! Выстроили стратегию тестирования, чтоб всё держалось, как швейцарские часы, а не разваливалось при первом же чихе. Сейчас разложу по полочкам, как ёбаные блины.
Первым делом — юнит-тесты, модульные, мать их. Берём стандартный testing пакет и начинаем долбить каждую функцию по отдельности. Главный фокус — табличные тесты, это ж просто песня! Запихал кучу граничных случаев в одну структуру, и поехали проверять. Красота, а не работа.
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-тесты, сквозные. Эмулируем полный путь пользователя, от нажатия кнопки до получения результата. Шлём настоящие HTTP-запросы к нашему API и смотрим, не обосрётся ли что. Пишем их на Go, а иногда используем Postman с Newman для автоматизации — гибкость, блядь, наше всё!
И конечно, CI/CD, без этого никуда. Весь этот цирк с тестами автоматически запускается в пайплайне, хоть в GitHub Actions, хоть где. Главное правило — если тесты падают, то твой код в основную ветку не попадёт, как ты ни пыжься. Жёстко, но справедливо, ёбаный рот этого кросавчега!
А ещё мы, как маньяки, следили за покрытием кода. Команда go test -cover — наш лучший друг. По критически важным пакетам гоняли планку в 80%, ниже — позор и публичная порка. Так что код у нас не просто работал, а работал, блядь, с гарантией, как танк!