Модульное тестирование для REST API в Go

Модульное тестирование для REST API в Go

18-10-2020 09:35:58
Создание API-интерфейсов RESTful на разных языках с разными подходами и шаблонами проектирования всегда было так же актуально, как и сложная кривая обучения. Это связано с тем, что в коде много абстракции, боль в начале проекта и множество других причин. При этом написание тест-кейсов для реализованных сервисов также является болью в шее.

Go дает вам привилегию писать API REST очень простым, элегантным и лаконичным способом. В дополнение к этому, модульное тестирование в Go также очень просто и для запуска тестовых случаев достаточно одной команды.

Я приложил коллекцию почтальонов для легкого импорта, sql dump, а также приложил файл readme, чтобы вы могли начать с простого назначения.

Итак, начнем

Было бы лучше, если мы рассмотрим пример для написания тестовых случаев. Допустим, мы хотим иметь Online Address Book приложение, в котором вы создаете адресную книгу, в которой у вас есть следующие поля:


package main

type entry struct {
ID int `json:"id,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
EmailAddress string `json:"email_address,omitempty"`
PhoneNumber string `json:"phone_number,omitempty"`
}


Мы будем считать , что у нас есть конечные точки GetEntries , GetEntryByID , CreateEntry, UpdateEntry и DeleteEntry .


GetEntries -> "/entries" -> Method GET
GetEntryByID -> "/entry?id=1234" -> Method GET
CreateEntry -> "/entry" -> Method POST
UpdateEntry -> "/entry" -> Method PUT
DeleteEntry -> "/entry" -> Method DELETE

Пакеты для использования в модульном тестировании в Go
testing - Это встроенный пакет Golang, который используется для реализации и запуска модульных / автоматических тестовых случаев. Он предназначен для работы с go testкомандой с необязательными параметрами, чтобы принять тестовый файл go.
net/http/httptest - Это также встроенный пакет Golang, который предоставляет привилегии для тестирования HTTP. В нашем случае мы хотели бы записать ответ от конечных точек и сделать соответствующие проверки.

ПРИМЕЧАНИЕ: Go избегает использования утверждений.
Написание юнит-тестов
Написание модульных тестов в Go может быть в одном пакете или в разных пакетах. Существует 2 критерия, по которым testing пакет Go идентифицирует контрольные примеры.

Имя файла должно заканчиваться на _test . Например - endpoints_test.go
Функция контрольных примеров должна начинаться со Test слова. Например -

func TestGetEntries(t *testing.T) {
....
}

Написание тестовых случаев для конечных точек API REST
Возьмем каждой конечной точке по одному , чтобы увидеть , как мы можем проверить все конечные точки из выше примера указанного , то есть GetEntries, GetEntryByID , GetEntryByIDNotFound , CreateEntry , EditEntry и DeleteEntry



Давайте начнем с написания тестовых случаев для следующего -

Тестовый пример GetEntries

func TestGetEntries(t *testing.T) {
req, err := http.NewRequest("GET", "/entries", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(GetEntries)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}

// Check the response body is what we expect.
expected := `[{"id":1,"first_name":"Krish","last_name":"Bhanushali","email_address":"krishsb@g.com","phone_number":"0987654321"},{"id":2,"first_name":"xyz","last_name":"pqr","email_address":"xyz@pqr.com","phone_number":"1234567890"},{"id":6,"first_name":"FirstNameSample","last_name":"LastNameSample","email_address":"lr@gmail.com","phone_number":"1111111111"}]`
if rr.Body.String() != expected {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), expected)
}
}


Примечание: указанный выше репозиторий Github не содержит отдельный файл go для каждого теста. Я указал все тестовые случаи в одном файле go.

Давайте пройдемся по get_entries_test.go строке за строкой -

Строка № 1 - обратите внимание на название функции здесь. Он начинается с Test того, что testing пакет распознает контрольный пример, а следующие слова в случае верблюда также начинаются с заглавной буквы. Функция имеет параметр, который является указателем на переменную testing пакета, Tкоторая означает, что это тестовый пример.
Строка № 2 - это создает новый запрос к /entries конечной точке.
Строка № 6 - Создает новый регистратор для записи ответа, полученного entries конечной точкой.
Строка № 8. Ударяет конечную точку с помощью рекордера и запроса.
Строка № 9 - Проверяет, является ли ответ 200 ОК.
Строка № 10 - отправляет теги ошибок как тестовый сбой.
Строка № 15 - это ожидаемый результат от конечной точки.
Строка № 16 - Проверьте, соответствует ли ответ ожидаемому.
Контрольный пример GetEntryByID-

func TestGetEntryByID(t *testing.T) {

req, err := http.NewRequest("GET", "/entry", nil)
if err != nil {
t.Fatal(err)
}
q := req.URL.Query()
q.Add("id", "1")
req.URL.RawQuery = q.Encode()
rr := httptest.NewRecorder()
handler := http.HandlerFunc(GetEntryByID)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}

// Check the response body is what we expect.
expected := `{"id":1,"first_name":"Krish","last_name":"Bhanushali","email_address":"krishsb2405@gmail.com","phone_number":"0987654321"}`
if rr.Body.String() != expected {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), expected)
}
}
GetEntryByIDNotFound Test Case-
func TestGetEntryByIDNotFound(t *testing.T) {
req, err := http.NewRequest("GET", "/entry", nil)
if err != nil {
t.Fatal(err)
}
q := req.URL.Query()
q.Add("id", "123")
req.URL.RawQuery = q.Encode()
rr := httptest.NewRecorder()
handler := http.HandlerFunc(GetEntryByID)
handler.ServeHTTP(rr, req)
if status := rr.Code; status == http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusBadRequest)
}
}
CreateEntry Test Case-
func TestCreateEntry(t *testing.T) {

var jsonStr = []byte(`{"id":4,"first_name":"xyz","last_name":"pqr","email_address":"xyz@pqr.com","phone_number":"1234567890"}`)

req, err := http.NewRequest("POST", "/entry", bytes.NewBuffer(jsonStr))
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
handler := http.HandlerFunc(CreateEntry)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}
expected := `{"id":4,"first_name":"xyz","last_name":"pqr","email_address":"xyz@pqr.com","phone_number":"1234567890"}`
if rr.Body.String() != expected {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), expected)
}
}



func TestEditEntry(t *testing.T) {

var jsonStr = []byte(`{"id":4,"first_name":"xyz change","last_name":"pqr","email_address":"xyz@pqr.com","phone_number":"1234567890"}`)

req, err := http.NewRequest("PUT", "/entry", bytes.NewBuffer(jsonStr))
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
handler := http.HandlerFunc(UpdateEntry)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}
expected := `{"id":4,"first_name":"xyz change","last_name":"pqr","email_address":"xyz@pqr.com","phone_number":"1234567890"}`
if rr.Body.String() != expected {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), expected)
}
}

Давайте пройдемся по create_entry_test.go строке

Строки 1–8 совпадают с приведенным выше описанием, где входные данные представляют собой jsonStrстроку JSON объекта ввода, и мы создаем новый запрос с новым методом POST .
Линия 9- Устанавливает заголовок с Content-Type быть application/json
Строка 9–22. Опять-таки, то же самое, когда запрос отсылается, а ответ сравнивается с ожидаемым. Если отправленный запрос 200 OK не совпадает или фактический ответ не равен ожидаемому ответу, то тестовый пример не пройден.

DeleteEntry test case-
func TestDeleteEntry(t *testing.T) {
req, err := http.NewRequest("DELETE", "/entry", nil)
if err != nil {
t.Fatal(err)
}
q := req.URL.Query()
q.Add("id", "4")
req.URL.RawQuery = q.Encode()
rr := httptest.NewRecorder()
handler := http.HandlerFunc(DeleteEntry)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}
expected := `{"id":4,"first_name":"xyz change","last_name":"pqr","email_address":"xyz@pqr.com","phone_number":"1234567890"}`
if rr.Body.String() != expected {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), expected)
}
}
DeleteEntry Контрольный пример снова совпадает с GetEntryByID контрольным, за исключением того, что метод запроса предназначен DELETE для DeleteEntry .

Запуск тестовых случаев
Давайте сначала запустим сервер, чтобы наши тестовые примеры попали в конечные точки, go run api.go entry.go учитывая указанный пример.

Для запуска всех тестовых случаев в наборе тестов вы можете сделать следующее:

go test -v


Запуск набора тестов
Чтобы запустить один тестовый пример, вы можете просто использовать сделать следующее:

go test -v -run

Выполнение одного контрольного примера, т.е. TestGetEntries
Следовательно, мы можем прийти к заключению, что теперь мы знаем, как писать тестовые примеры для RESTful API в Go.
Если вам понравился этот пост, поделитесь им со всеми своими друзьями по программированию!


САМОЕ ОБСУЖДАЕМОЕ

...
Титан в воздухе
18-10-2020 09:35:58
Это не самолет – это Мрия!!!...
...
Лучшие бюджетные ноутбуки на 2020 год
18-10-2020 09:35:59
Как выбрать лучший ноутбук в 2020 году...
...
Технологические тенденции в 2020 году
18-10-2020 09:35:59
Технологи будущего уже сегодня...
...
Самостоятельная поездка автомобиля под управлением искусственного интеллекта
18-10-2020 09:35:58
Самый далекий перезд без водителя в истории искуственного интеллекта....
...
Ford Bronco наконец дебютирует 9 июля
18-10-2020 09:35:58
После задержек, связанных с коронавирусом, у долгожданной Бронко Форда официально объявлена ​​дата....
...
Tesla Semi готова к «массовому производству»
18-10-2020 09:35:58
Но генеральный директор не указал точные сроки начала производства электрического грузовика....
...
25 самых продаваемых легковых автомобилей, грузовиков и внедорожников 2020 года (пока)
18-10-2020 09:35:58
Хотя пандемия коронавируса привела к хаосу в продажах автомобилей, мы подсчитали рейтинг самых продаваемых в первом квартале....
...
Электрический Mustang
18-10-2020 09:35:58
Ford представляет электрический Mustang с «потрясающим» ускорением...
...
Cамые большие дизайнерские моменты 2018 года
18-10-2020 09:35:59
Мы попросили дизайнеров рассказать нам, что они считают самой важной вещью, которая произошла в отрасли в этом году....
...
Airtable
18-10-2020 09:35:59
Простая в использовании система управления реляционными базами данных...

НАШИ РЕКОМЕНДАЦИИ

Toyota Supra в 2021 году
Toyota Supra в 2021 году
Toyota Supra 2020 года не получит дооснащения, чтобы соответствовать увеличению мощности в 2021 году...
9 различных вариантов использования console log
9 различных вариантов использования console log
Каждый из нас использовал console.logдля отладки больше, чем нам хотелось бы признать....
Расслабление, снятие стресса и развитие сознания
Расслабление, снятие стресса и развитие сознания
Иногда в жизни бывают дни.......
Послушай других и сделай наооборот
Послушай других и сделай наооборот
Основатель Tesla и SpaceX воплотил в жизнь одну идею, противоречащую общепринятому мнению, и это помогло ему заработать миллиарды....
Красный флаг или работа не Вашей мечты.
Красный флаг или работа не Вашей мечты.
В последнее время развелось (были всегда) много организаций на рынке - которые надо обходить стороной....


ИНТЕРЕСНОЕ

Понимание карты и набора в JavaScript
Понимание карты и набора в JavaScript
Эта статья была изначально написана для DigitalOcean ....
Lazareth Wazuma от Феррари
Lazareth Wazuma от Феррари
Lazareth Wazuma V8F Quad – Engine By Ferrari...
20 самых важных секретов настоящих отношений
20 самых важных секретов настоящих отношений
Не простые отношения между женщинами и мужчинами...
Работа в Швеции
Работа в Швеции
Компании в Швеции переходят на 6-часовые рабочие дни и добиваются удивительных результатов...


ЛУЧШИЕ РЕЙТИНГИ

Набор массы
Набор массы
Базовые принципы для новичков
Ежедневные 15-минутные прогулки способны кардинально изменить ваше тело
Ежедневные 15-минутные прогулки способны кардинально изменить ваше тело
Всем известно, как положительно влияют на организм...
Правила для наращивания мышечной массы
Правила для наращивания мышечной массы
Зная интенсивность физических упражнений...
Раскачать грудь
Раскачать грудь
Обычно грудные растут хорошо у тех...

АКТУАЛЬНОЕ

Будущее уже с нами Galaxy Fold
CEO продвижение
Отношения с мужчинами
Раздевалка
Лишь плохие начальники ожидают от своих подчиненных постоянной занятости
Как научиться читать быстрее
Выбирай того, кто ежедневно пишет тебе «С добрым утром»
США скрывают правду о пришельцах
Свечение от ракеты SpaceX американцы приняли за НЛО
Путешествие из Австрии в Италию

ЧИТАЙТЕ ТАКЖЕ

18-10-2020 09:35:59 (120052)
Какую одежду носят манхэттенские модницы летом
Возможно эта новость тебе еще неизвестна
18-10-2020 09:35:59 (120050)
Время сгибаемых смартфонов еще не пришло
Эксперт по технологиям издания Mashable Стэн Шредер написал колонку...
18-10-2020 09:35:59 (120049)
Успешные стартапы, которые начинали как сторонние проекты
Apple, Facebook, Google, SpaceX ...
18-10-2020 09:35:59 (120051)
Надо стараться быть с теми, кто к нам хорошо относится
Маленький гимназист очень плохо учился...
18-10-2020 09:35:59 (120044)
Стопроцентная диета для похудения или питание наоборот
Сделай все наоборот....
18-10-2020 09:35:59 (120046)
Невероятная 12-месячная трансформацией тела
Звезда фитнеса из Сиднея Софи Аллен рассказывает о своей трансформации..