api-tests - утилита для автоматизированного тестирования REST API с поддержкой HTTP запросов и websocket соединений.
Для всех, кто хочет быть уверен что все методы его API работают как требуется.
Утилита на основе конфигурационных файлов отправляет запросы к серверу и сверяет ответ с заданным. Можно описывать последовательные запросы с передачей промежуточных данных.
Все настройки описываются в файлах в формате YAML, отдельный файл - отдельное действие. Файлы можно группировать в папки. Тесты в отдельных папках не зависят друг от друга, тесты в одной папке взаимосвязаны и сортируются между собой по имени файла.
В описаниях тестов доступны переменные, которые можно принимать из переменных окружения, из файла init.yml, либо создавать в процессе выполнения тестов.
Отдельные тесты можно группировать помещая их в отдельную папку. Порядок запуска отдельных групп не регламентирован и может в будущем измениться.
Так же можно запускать выполнение только определенных групп по маске передавая параметр --pattern, в этом параметре можно указать регулярное выражение.
Например, может быть такая структура (тесты с проверкой логина и регистрации):
.
└── auth
├── login
│ ├── 1-login.yml
│ ├── 2-check-auth.yml
│ └── init.yml
└── register
├── 1-register.yml
├── 2-check-auth.yml
└── init.yml
Пример запуска теста из папки auth/login:
api-tests --pattern auth/login
В папке с тестами можно создать файл init.yaml или init.yml. Этот файл обрабатывается перед запуском группы. В данный момент в файле можно только определить набор переменных для тестов - в секции store.
Пример файла init.yml с объявлением трех переменных:
store:
host: localhost
login: ivan
password: qwrty123
В файле теста могут быть несколько корневых секций:
- name - имя теста. Обязательное поле.
- request - http запрос к серверу.
- response - валидация ответа http запроса.
- receive - принятие сообщения из websocket канала.
- message - валидация сообщения из websocket канала.
Секция описывает HTTP запрос к серверу.
Может содержать следующие параметры:
- method - тип запроса (GET, POST, PUT...). По-умолчанию GET.
- protocol - тип запроса: http или websocket. По-умолчанию http.
- url - адрес запроса, например:
https://www.google.com/search?q=tests
или, в случае websocket соединения -wss://site.com/ws-open
. - headers - список отправляемых заголовков.
- body - тело запроса.
- timeout - время ожидания ответа в секундах. По-умолчанию - 5.
- channel - имя соединения. Websocket соединение сохраняется с этим именем и в будущем его можно использовать.
Пример файла с request. Отправляется POST запрос на адрес https://example.com/api/reports
, в заголовках отправляется Content-Type: application/json
, в теле запроса {"begin":1663099200000,"end":1663271999999}
, время ожидания ответа - 30 секунд.
name: Отправляем POST запрос
request:
method: POST
url: 'https://example.com/api/reports'
headers:
Content-Type: application/json
body: '{"begin":1663099200000,"end":1663271999999}'
timeout: 30
Секция может состоять из нескольких элементов:
- headers - валидация полученных заголовков - набор правил.
- code - валидация кода ответа - набор правил. Всегда проверяется только числовой статус (200, 301, 404 и другие...).
- body - валидация тела ответа - набор валидаторов
Валидатор регламентирует способ обращение к данным и правила, описывается параметрами type и rules. Параметр type может принимать значения из списка:
- json - можно обращаться к элементам по ключу.
- string - ответ сверяется как единое целое.
Параметр rules - это набор правил.
Секция позволяет получать сообщение из вебсокет канала по фильтру. Имеет параметры:
- channel - имя вебсокет соединения
- timeout - время в секундах, сколько ждать сообщения
- filter - набор валидаторов для фильтра. Сообщение считается подходящим, если ни один из фильтров не выдал ошибку.
Секция валидирует сообщение, которое было получено в receive. Содержит набор правил.
Правило описывается параметрами:
- type - тип значения, которое проверяется (string, boolean, float, integer, jwt, hex, object, array)
- key - ключ значения
- equal - проверка значения на равенство
- not-equal - проверка значения на не равенство
- less - проверка на то, что значение меньше, чем указано
- greater - проверка на то, что значение больше, чем указано
- prefix - проверка на то, что значение содержит префикс
- suffix - проверка на то, что значение содержит суффикс
- required - указание на то что значение обязательно должно быть. По-умолчанию - true
- store - сохранение значения с указанным именем
- fields - правила для проверки вложенных значений (только для типов array и object)
Пример файла с валидацией ответа. Проверяется http код - должен быть 200 OK. Тело ответа проверяется как JSON. В ответе должны быть поля success типа boolean, в котором должно быть true, и поле data с JWT токеном, значение сохраняем в хранилище с именем token.
name: Авторизация
request:
method: POST
url: 'https://example-api.com/api/auth/login'
headers:
Content-type: application/json
body: '{"username":"roman","password":"qwerty123"}'
response:
code:
- equal: 200
body:
- type: json
rules:
- key: success
type: boolean
equal: true
- key: data
type: JWT
store: token
Еще пример с проверкой ответа на равенство.
name: Список пользователей
request:
method: GET
url: 'https://example-api.com/api/users'
response:
body:
- type: string
rules:
- equal: '["roman","ivan"]'
Пример с валидатором ответа как строка и проверкой того, что результат меньше, чем 10
name: Список пользователей
request:
method: GET
url: 'https://example-api.com/api/status'
response:
body:
- type: string
rules:
- type: integer
less: 10
Вложенные поля подерживаются только через валидатор json и с типами array и object.
Скажем некий API отдает такой ответ:
{
"data": {
"accounts": [
{
"name": "Ivan",
"email": "[email protected]"
},
{
"name": "Roman",
"email": "[email protected]"
}
]
}
}
Нам надо получить второй аккаунт и проверить имя. Это делается так:
# Имя теста
name: Получить второй аккаунт и проверить имя
# Отправляемый запрос
request:
method: GET
url: 'https://{{.TESTS_HOST}}/api/accounts'
# Валидация ответа
response:
body:
- type: json
rules:
# Обращаемся к data и получаем его как объект
- key: data
type: object
fields:
# Внутри data получаем accounts как массив
- key: accounts
type: array
fields:
# Получаем элемент по индексу 1 (второй элемент)
- key: 1
type: object
fields:
# Получаем поле name как string, проверим что там текст Roman
- key: name
type: string
equal: Roman
К элементам массива можно обращаться по индексу, можно индекс не указывать - тогда пройдет валидация всех элементов массива.
Во всех параметрах можно использовать переменные. Чтобы вставить значение переменной, вначале пишется {{.
, после имя переменной, и потом }}
. Например {{.token}}
.
Переменные можно обратиться к переменным окружения, но только к тем, имена которых начинаются на TESTS_
.
Переменные можно определить в init файле, можно создавать во время выполнения указанием store у правила.
Переменные привязаны к группе тестов, это значит что внутри группы можно передавать данные между отдельными тестами, но не получится передавать данные между отдельными группами.
Пример двух файлов - в одном получаем токен, в другом используем. В тестах используем переменную окружения TESTS_HOST. В первом тесте создаем переменную token, во втором тесте - используем её.
1-auth.yml:
name: Авторизация
request:
method: POST
url: 'https://{{.TESTS_HOST}}/api/auth/login'
body: '{"username":"roman","password":"qwerty123"}'
response:
body:
- type: json
rules:
- key: data
type: JWT
store: token
2-check-token.yml:
name: Авторизация
request:
method: GET
url: 'https://{{.TESTS_HOST}}/api/client/user/settings'
headers:
Authorization: 'Bearer {{.token}}'
response:
body:
- type: json
rules:
- key: success
type: boolean
equal: true