Пишем gRPC автотесты на Go с Allure отчетом
Вступление
В данной статье разберем, как писать gRPC автотесты с использованием языка Go, а также сделаем Allure отчет.
Перед тем, как читать статью, нужно базово понимать некоторые термины:
Без понимания вышеописанного будет сложно разобраться о чем идет речь.
Requirements
Для написания автотестов нам понадобится gRPC сервер. Поискал на просторах интернета открытые gRPC сервера, но ничего нашел. Поэтому придется написать свой сервер, который можно будет запустить локально и уже на него писать автотесты.
Исходный код сервера расположен на моем github. Инструкции по установке и настройке сервера прилагаются. Если у вас нет необходимости как-то изменять/расширять имеющийся контракт, то можно сразу пропустить секцию Setup protobuf.
Запустить сервер можно двумя способами:
- По этой инструкции установить все необходимые зависимости на вашу OS и запустить сервер;
- Либо запустить сервер внутри docker, но тогда нужно посмотреть эту инструкцию.
После запуска сервера локально он будет доступен по адресу localhost:8000 или же 127.0.0.1:8000. До написания автотестов неплохо было бы иметь клиент, с помощью которого можно будет "потыкать" сервер. Таким клиентом может быть Postman, но только десктопный вариант, тк в браузере gRPC не может быть использован. Инструкции о том, как настроить Postman на работу c gRPC.
Теперь посмотрим на структуру контракта, который находится в репозитории с сервером. Этот же контракт мы будем использовать в автотестах:
В контракте описаны методы:
- GetArticle - получение статьи по id. Запрос GetArticleRequest и ответ GetArticleResponse;
- CreateArticle - создание статьи. Запрос CreateArticleRequest и ответ CreateArticleResponse;
- UpdateArticle - изменение статьи. Запрос UpdateArticleRequest и ответ UpdateArticleResponse;
- DeleteArticle - удаление статьи. Запрос DeleteArticleRequest и ответ DeleteArticleResponse;
Также, обратите внимание, что есть модель Article, которая используется в методах создания, обновления и получения статьи. Эта модель специально вынесена отдельно, чтобы лишний раз не дублировать код.
Выше приведен очень простой контракт на стандартные CRUD операции. На реальных проектах контракты могут быть намного сложнее, но для тестов нам подойдет и такой.
Сторонние библиотеки, которые понадобятся:
Есть еще ряд библиотек без которых никуда, но все они уже встроены в go.
Configuration
Перед написанием автотестов нам необходимо добавить файл с конфигурациями, в котором опишем основные настройки
Напишем настройки для сервиса статей и логгера; скорее всего, для вашего проекта понадобится больше конфигов, поэтому можете добавить их в этот infrastructure/config-local.yml файл.
Лайфхак. Если есть необходимость запускать автотесты сразу на нескольких окружениях, то для этого можно создать файлы настроек под каждое из окружений, как, например, в нашем случае:
- config-dev.yml
- config-local.yml
- config-stable.yml
Далее, в зависимости от переменной окружения ENV, будем брать нужный файл и загружать настройки. Подробнее разберем ниже, когда будем описывать чтение настроек в файле utils/config/internal.go
Напишем модели, внутрь которых будут "помещаться" конфиги из файла infrastructure/config-{env}.yml
Теперь напишем парсер, который будет читать конфиги и "раскладывать" их по моделям из utils/config/models.go
Обратите внимание, что мы динамически подставляем окружение в путь до настроек "../infrastructure/config-%s.yml"
Service
Добавим методы для взаимодействия с нашим сервером, но перед этим необходимо сделать gRPC клиент
Функция GetGrpcClient будет принимать настройки Host, Port для инициализации клиента, создавать клиент и возвращать его
Теперь можно написать клиент для сервиса ArticlesService. Структура, которой будем придерживаться:
- client.go - описываем клиент и билдер, который будет конструировать клиент;
- api.go - будет описывать методы для взаимодействия с сервером. По сути, это будет просто обертка, внутри которой накинем логи, проверки;
- steps.go - добавляем к методам из api.go allure шаги, описание, параметры и всё, что связано с отчетом. Можете пропустить этот слой, если вам не нужны шаги отчета либо сам отчет;
Из чего состоит каждый метод:
- Логирование запроса, который отправляется на сервер;
- Вызов удаленной процедуры;
- Обработка ошибки с помощью gomega. В случае, если сервер ответил ошибкой, то тест упадет уже на этом этапе. Если вам наоборот нужно вызвать ошибку, то можно написать другой метод и добавить проверку на то, что была получена ошибка g.Expect(err).Should(gomega.HaveOccurred(), "GetArticle error");
- Если все хорошо и ошибки не случилось, то логируем ответ от сервера.
Отлично, шаги написали, теперь стоит добавить утилиты, которые понадобятся для написания тестов.
Logger
Логирование - очень важный атрибут и без него будет сложно понять, что сейчас делает тест, на каком именно шаге он упал, что ответил сервер.
В качестве библиотеки для логирования будем использовать zap
Напишем конфиги для логгера и билдер
- utcTimeEncoder, newEncoderConfig, newConfig - по сути это просто утилиты, которые помогают настроить логгер;
- NewLoggerService - будет создавать и настраивать сервис логгера;
- NewPrefix - будет использоваться для добавления логгера к конкретному сервису, как мы это делали чуть ранее.
Components/dependency injection
Будем использовать библиотеку dig для реализации dependency injection.
Зачем нам это нужно? Давайте разберем на примере автомобиля. Автомобиль состоит из колес, руля, двигателя, электроники, бензобака и т.д. Но конечного клиента, то есть водителя, не волнует, как, где, кем и когда были добавлены/созданы эти детали, водителю нужен лишь интерфейс управления машиной/двигателем. Водителю важен лишь конечный результат - в машину можно сесть, завести и поехать. Аналогично и с автотестами, - для них требуется много сервисов/клиентов, например, базы данных, внутренние или внешние сервисы, рестовые апишки, вольт с кредами и т.д. По сути говоря, внутри теста не важно, кто, где и как инициализировал этих клиентов, нужен лишь интерфейс, чтобы мы могли пойти в базу данных, отправить запрос на сервер, получить какие-то креды и т.д.
Опишем компоненты, которые потом будем использовать в тестах:
Теперь напишем билдер, который будет инициализировать компоненты:
Подробнее про использование библиотеки dig вы можете почитать тут
Assertions
- Проверить, что значения равны/не равны;
- Обработать ошибку;
- Асинхронные проверки, например, нужно подождать, когда сервис вернет нужный статус код.
У gomega еще есть много крутых фичей и настроек, но нам будет достаточно вышеперечисленного.
Для начала напишем gomega инициализатор, который будет инициализировать gomega и добавлять базовые настройки:
Теперь нужно описать базовые проверки, которые будут использоваться во всем проекте:
Нам понадобится только одна проверка AssertToEqual, но в вашем проекте, скорее всего, понадобится намного больше проверок по типу AssertIsNotNil, AssertIsNil, AssertToHaveLen и т.д. Их также можно будет объявить в этом файле и далее использовать по всему проекту.
Лайфхак. Обратите внимание на то, каким образом значение expected подставляется в шаблон шага "Checking that '%s' equals to '%s'". Функция PrettifyValue специально используется, чтобы придать значению expected читабельный вид. Если использовать общие проверки для разных типов данных, то такой "Checking that '%s' equals to '%s'" шаблон форматирования будет нормально работать только для строк. Также в go есть поинторы, которые тоже будут отображаться некорректно. Конечно же, вы можете написать проверку под каждый тип, например, AssertToEqualString, AssertToEqualInt, AssertToEqualFloat и т.д., но тогда придется писать очень много дублированных проверок и под каждую нужно будет делать свой шаблон. В нашем случае функция PrettifyValue получает на вход любое значение и возвращает уже отформатированный, человеко-читабельный вариант в формате type[value], например, string[MyString], int8[4], int32[1234] и т.д. Вы можете использовать другое решение, это лишь пример, как можно, не дублируя проверки, сделать красивые шаги в отчете.
Теперь напишем конкретные проверки уже для сервиса ArticlesService, а именно для модели Article
Лайфхак. Если вы пишете тесты для gRPC сервиса, то у вас наверняка есть много похожих моделей в проекте, которые используются в разных сервисах или могут быть вложены в другие модели. Для общих моделей сразу стоит делать отдельные проверки, например, есть модель User, тогда делаем проверку CheckUser и используем ее внутри других проверок, например, так:
Это сэкономит больше времени в будущем при написании новых проверок или же, если нужно будет вносить изменения в уже имеющиеся проверки.
Utils
Теперь почти все готово к написанию автотестов, добавим лишь несколько утилит, которые помогут в написании тестов.
Добавим функцию, которая будет подготавливать нужные для теста компоненты; в нашем случае components, gomega.
Также вынесем все части касающиеся отчета в отдельный пакет, чтобы потом их можно было использовать в других автотестах.
Testing
Теперь можно писать автотесты. Всего у сервиса ArticlesService, четыре метода, значит напишем четыре простых позитивных теста
Report
Перед запуском тестов убедитесь, что сервер запущен и работает
Запустим тесты и посмотрим на отчет:
Теперь запустим отчет:
Либо можете собрать отчет и в папке allure-reports открыть файл index.html:
Заключение
Весь исходный код проекта с автотестами расположен на моем github.
Весь исходный код проекта с сервером расположен на моем github.