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 } } } """)