1.6. Разработка тестов¶
Тестирование приложений для системы TDG преследует две основные цели:
Проверка логики модулей (юнит-тесты);
Проверка продукта на соответствие требованиям (интеграционные тесты).
Юнит-тесты обычно синтетические и предназначены для исчерпывающего покрытия критических частей кода и последующей автоматической проверки на регрессии. Самая большая польза от юнит-тестов — выявление ситуаций, в которых модуль может получить разнообразный неверный «ввод» от пользователя. В таких случаях юнит-тесты помогают убедиться, что модуль реагирует на неверный ввод корректно. Если система приняла неверные по форме/содержанию данные или, наоборот, не приняла верные данные (посчитав их неверными), то юнит-тесты — идеальное средство проверки подобных сценариев.
Интеграционные тесты предназначены для проверки функциональности сложных элементов и для проверки на соответствие требованиям заказчика. Если поступает новое требование в формате пользовательской истории, то оно обязательно должно быть проверено интеграционным тестом, в котором должна быть сделана ссылка на требование.
Прежде чем разрабатывать собственные тесты, ознакомьтесь с существующими и запустите их.
1.6.1. Запуск тестов¶
Чтобы запустить существующие юнит-тесты, в корне проекта:
Установите все зависимости при помощи скрипта:
./deps.sh
Он установит в папку
.rocks
все модули, нужные для запуска.Запустите юнит-тесты:
tarantool unit.lua
Команда выполнит по очереди все юнит-тесты из папки
test/unit
.
Чтобы запустить существующие интеграционные тесты:
Установите python3, pip и несколько python-библиотек. Весь список зависимостей приведен в файле
requirements.txt
в корне проекта. Чтобы установить их все глобально, используйте pip. Например:sudo pip3 install -r requirements.txt
Примечание
Как альтернативу можно настроить pipenv и сделать для прогона тестов отдельную среду.
Запустите интеграционные тесты:
pytest
1.6.2. Разработка юнит-тестов¶
Юнит-тесты сгруппированы по подсистемам в подпапках test/unit
.
Чтобы тест был включен в общий прогон, имя файла должно начинаться с
test_
и иметь расширение .lua
, например, test_ddl.lua
.
Команда tarantool unit.lua
найдет в упомянутой папке файлы с
такими именами и автоматически исполнит их.
Юнит-тесты используют модуль Tarantool tap
. Чтобы написать новый тест, используйте следующий код:
#!/usr/bin/env tarantool
local tap = require('tap')
local test = tap.test("validation")
test:plan(5) --- замените цифру на количество тестов в модуле
--- здесь будут тесты
os.exit(test:check() and 0 or 1)
Чтобы написать сами тесты, достаточно вызывать у объекта test
функции проверки:
test:isnil()
test:isstring()
test:isnumber()
test:istable()
test:isboolean()
test:isudata()
test:iscdata()
Если в эти функции будут переданы данные, не соответствующие ожидаемым, тест будет прерван с ошибкой и поясняющим сообщением.
Примечание
Чтобы быстро понять, как писать юнит-тесты, лучше всего посмотреть в реализацию существующих.
Если в тестах требуется использовать box
-функции, используйте
следующий код:
local tarantool = require('test.unit.tarantool').new()
tarantool:start()
--- здесь будут тесты
local success = test:check()
tarantool:stop()
os.exit(success and 0 or 1)
1.6.3. Разработка интеграционных тестов¶
Интеграционные тесты основаны на pytest и «заточены» под тестирование логики приложения с возможным подключением логики кластера.
Для запуска каждого файла с тестами:
Сначала поднимается «с нуля» TDG или кластер из узлов с разными ролями.
Затем ко всем возможным узлам применяется соответствующая конфигурация.
Наконец, по очереди запускаются тест-кейсы.
Интеграционные тесты находятся в подпапках test/integration
.
В отличие от юнит-тестов, они сгруппированы не по подсистеме, а по
логической «близости» тестов друг к другу. Основной критерий группировки
— тесты в одной подпапке имеют одну и ту же стартовую конфигурацию.
Чтобы создать новую группу тестов:
Добавьте папку в
test/integration
и в ней сделайте подпапкиconfig
иdata
.В
config
поместите все конфигурационные файлы для TDG иconnector
. При исполнении этой группы тестов, такие файлы будут автоматически загружены в качестве начальной конфигурации.Дополнительно к папке
config
можно создать рядом папкуdata
и положить туда тестовые данные, например, большие XML или JSON-объекты. Это поможет не прописывать данные в коде теста в явном виде.Создайте файл с именем, начинающимся с
test_
и расширением.py
. Такие файлы автоматически включаются в прогон.В файле используйте следующий код:
#!/usr/bin/env python3 import pytest import os def test_something(server, datadir): # тут можно писать логику теста assert something == something_else # тут можно добавить еще тестов
Здесь функция-тест test_something
имеет параметры server
и datadir
.
Эти параметры называются «фикстуры», и они должны быть у каждой функции-теста.
Фикстуры — это способ удобно использовать в тестах библиотечную
функциональность. О них можно прочитать в
документации по pytest.
При указании в параметрах datadir
, можно получить полный путь
к папке, в которой лежат тестовые данные текущей группы тестов.
Когда pytest видит в параметрах тестовой функции server
:
Он автоматически стартует TDG.
Применяет к нему конфигурацию.
Передает объект-обертку («враппер»), позволяющую удобно делать запросы (HTTP, SOAP, GraphQL и другие) к TDG.
1.6.3.1. API объекта-обертки¶
Объект-обертка сервера («враппер») поддерживает запросы через HTTP, SOAP и GraphQL:
server.post(path, data, json)
— посылаетpost
-запрос по путиpath
. Можно указать либо строкуdata
для текстовых запросов, либоjson
для JSON-запросов;server.soap(data)
— послает SOAP-запрос, гдеdata
— текст запроса;server.graphql(query)
— послает GraphQL-запрос, гдеquery
— текст запроса. В ответ функция либо бросает исключение при ошибке, либо возвращает объект с результатом. graphql для TDG разделён на схемы. Для доступа к даннымschema = 'default'
или не указывается. Для доступа к функциям администрированияschema = 'admin'
. Например, запрос данных:server.graphql(""" query { User(country:"USA") { fullname } } """)
Пример вызова администрирования TDG, изменение модели:
obj = server.post('/graphql', json={ "query": "mutation set_model($model:String!) { model(model: $model) }", "variables": { "model": json.dumps(model) }, "schema": "admin" })
server.cluster_graphql(query)
— посылает GraphQL-запрос для управления кластером. Например,obj = server.cluster_graphql(""" { servers { uri replicaset { roles } } } """)