Ответ
Тестирование HTTP-взаимодействий в Go можно разделить на две основные категории: тестирование сервера (ваших хендлеров) и тестирование клиента (кода, который обращается к внешним API).
1. Модульное тестирование HTTP-серверов (хендлеров)
Для тестирования хендлеров в изоляции, без запуска реального веб-сервера, используется стандартный пакет net/http/httptest
.
httptest.NewRequest()
: Создает mock-запрос (*http.Request
) для передачи в ваш хендлер.httptest.NewRecorder()
: Создает mock-объектhttp.ResponseWriter
, который записывает код ответа, заголовки и тело, чтобы вы могли их проверить.
package main
import (
"net/http"
"net/http/httptest"
"testing"
)
// Тестируемый хендлер
func MyHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
}
func TestMyHandler(t *testing.T) {
// 1. Создаем запрос к нашему хендлеру
req := httptest.NewRequest(http.MethodGet, "/test", nil)
// 2. Создаем ResponseRecorder для записи ответа
rr := httptest.NewRecorder()
// 3. Вызываем хендлер напрямую
MyHandler(rr, req)
// 4. Проверяем результат
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
}
expected := `OK`
if rr.Body.String() != expected {
t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
}
}
2. Модульное тестирование HTTP-клиентов
Когда ваш код делает запросы к внешним сервисам, тесты не должны зависеть от доступности этих сервисов. Для этого используется httptest.NewServer
, который запускает настоящий, но локальный и временный HTTP-сервер для ваших тестов.
package main
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
// Функция, которую мы тестируем (она делает внешний запрос)
func GetUserData(apiURL string) (string, error) {
resp, err := http.Get(apiURL + "/users/1")
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
return string(body), nil
}
func TestGetUserData(t *testing.T) {
// 1. Создаем тестовый сервер
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Проверяем, что клиент пришел по правильному пути
if r.URL.Path != "/users/1" {
t.Errorf("Expected to request '/users/1', got '%s'", r.URL.Path)
}
// Отдаем заранее подготовленный ответ
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"id": 1, "name": "John Doe"}`))
}))
defer server.Close() // Важно закрыть сервер после теста
// 2. Вызываем нашу функцию, передав ей URL тестового сервера
body, err := GetUserData(server.URL)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// 3. Проверяем, что функция правильно обработала ответ
expected := `{"id": 1, "name": "John Doe"}`
if body != expected {
t.Errorf("unexpected body: got %s want %s", body, expected)
}
}
3. Ручное и интеграционное тестирование
Для проверки работающего приложения используются инструменты вроде Postman, Insomnia или консольной утилиты curl. Они позволяют отправлять реальные запросы к запущенному приложению и анализировать ответы.
Ключевые аспекты для проверки:
- Код состояния HTTP (Status Code):
200 OK
,404 Not Found
,500 Internal Server Error
и т.д. - Заголовки (Headers):
Content-Type
,Authorization
и другие. - Тело ответа (Response Body): Корректность данных в формате JSON, XML или другом.
- Обработка ошибок: Как система ведет себя при неверных входных данных.
- Время ответа: Убедиться, что запросы выполняются за приемлемое время.