Ответ
Интеграционные тесты проверяют взаимодействие между несколькими компонентами системы (например, сервис и база данных). В Go для этого используются следующие подходы и инструменты:
1. Тестирование HTTP-слоя с net/http/httptest
Стандартная библиотека предоставляет пакет httptest для тестирования HTTP-хендлеров без реального запуска сервера. Он позволяет создавать моки запросов (NewRequest) и записывать ответы (NewRecorder).
Пример:
func TestGetUserHandler(t *testing.T) {
// Создаем мок-запрос к нашему хендлеру
req := httptest.NewRequest(http.MethodGet, "/users/1", nil)
w := httptest.NewRecorder()
// Предположим, что GetUserHandler - это наш http.Handler
GetUserHandler(w, req)
// Проверяем статус-код ответа
if w.Code != http.StatusOK {
t.Errorf("Expected status OK; got %v", w.Code)
}
// Проверяем тело ответа
var user User
if err := json.NewDecoder(w.Body).Decode(&user); err != nil {
t.Fatalf("Could not decode response: %v", err)
}
if user.ID != 1 {
t.Errorf("Expected user ID 1; got %d", user.ID)
}
}
2. Работа с внешними зависимостями (БД, кэши)
Для интеграционных тестов с базами данных (PostgreSQL, MySQL) или кэшами (Redis) лучшей практикой является запуск этих зависимостей в Docker-контейнерах на время теста. Это гарантирует чистое и изолированное окружение.
- Инструменты: Библиотека
testcontainers-goсильно упрощает управление жизненным циклом Docker-контейнеров прямо из Go-кода.
3. Разделение тестов с помощью Build Tags
Интеграционные тесты обычно медленнее юнит-тестов, поэтому их часто запускают отдельно. Для этого используются build-теги.
-
В начало файла с интеграционным тестом добавляется специальный комментарий:
//go:build integration package my_test -
Запуск тестов выполняется с указанием тега:
# Запустить только интеграционные тесты go test -v -tags=integration ./... # Запустить все тесты, кроме интеграционных go test -v ./...
Ответ 18+ 🔞
А, интеграционные тесты в Go! Ну, это когда ты проверяешь, как твои куски кода, которые по отдельности вроде не дураки, начинают друг с другом общаться. Типа, сервис пытается поговорить с базой данных, а она ему в ответ — молчание, ёпта, или хуже того, ошибку какую-нибудь. Вот чтобы такого не было, мы их и тестируем.
1. Тестирование HTTP-хендлеров с httptest
Стандартная библиотека, блядь, не лыком шита, дала нам пакет net/http/httptest. Это такая волшебная палочка, чтобы не поднимать настоящий сервер, а просто помахать перед хендлером фальшивым запросом и посмотреть, не обосрётся ли он. Создаёшь запрос-пустышку (NewRequest) и рекордер, который всё запишет (NewRecorder).
Смотри, как это выглядит:
func TestGetUserHandler(t *testing.T) {
// На коленке слепили запрос, типа "дай мне юзера с ID 1"
req := httptest.NewRequest(http.MethodGet, "/users/1", nil)
w := httptest.NewRecorder()
// Суём это всё нашему хендлеру, как котёнка под нос
GetUserHandler(w, req)
// А теперь проверяем, не послал ли он нас нахуй с кодом 500
if w.Code != http.StatusOK {
t.Errorf("Ожидали статус ОК, а получили %v", w.Code)
}
// Пытаемся вытащить из ответа структуру
var user User
if err := json.NewDecoder(w.Body).Decode(&user); err != nil {
t.Fatalf("Ответ такой кривой, что даже json распарсить не могу: %v", err)
}
// Ну и наконец, тот ли юзер пришёл, или это левый чел
if user.ID != 1 {
t.Errorf("Ожидали юзера с ID 1, а пришёл какой-то левак с ID %d", user.ID)
}
}
2. Базы данных, Redis и прочая внешняя хуйня
Тут уже посерьёзнее. Если тест должен тыкать в настоящую PostgreSQL или Redis, то лучший способ — поднять эту банду в Docker-контейнере на время теста. Чистое поле, никаких левых данных от прошлых запусков.
- Инструмент в помощь:
testcontainers-go. Эта штука, блядь, просто бомба. Она из кода на Go умеет запускать контейнеры, ждать, пока база поднимется, и потом аккуратно прибить всё после тестов. Красота, а не жизнь.
3. Как не запускать эти долгие тесты каждый раз
Интеграционные тесты — они же медленные, ёпта. Запускать их при каждом go test ./... — это как каждый раз перезагружать компьютер, чтобы проверить, работает ли калькулятор. Поэтому их отсекают с помощью build-тегов.
-
В самый верх файла с интеграционными тестами пишешь заклинание:
//go:build integration package my_test -
А запускаешь их только когда надо, явно указав тег:
# Запустить ТОЛЬКО интеграционные тесты (готовься ждать) go test -v -tags=integration ./... # Запустить все БЫСТРЫЕ тесты (юниты), интеграционные проигнорятся go test -v ./...
Вот так вот, нехитрая магия, но без неё — пиши пропало, будешь полдня ждать, пока тесты прогуляются.