Создаём нагрузочный тест для REST-сервиса на SoapUI.

Одним из наиболее распространенных инструментов для тестирования REST-сервисов на данный момент (на ряду, пожалуй, с Postman’ом), является утилита под названием SoapUI. Как следует из её названия, основное её предназначение - это тестирование (либо простое взаимодействие) веб-сервисов, работающих по протоколу SOAP. Однако, инструмент также годится и для операций с REST-сервисами. Как инструмент тестирования SoapUI подходит как для функциональных, так и для нагрузочных тестов, рассмотрением которых мы в сегодняшней статье и займемся.

Способности SoapUI в области эмуляции нагрузки не отличаются каким-либо богатством. Для всеобъемлящего тестирования не достает возможности контролировать порядок исполнения шагов и в целом то что относится к логике исполнения (циклы, условные ветвления, и т.д.), однако для проверки приложения на соответствие базовым требованиям по времени отклика и корректности ответа - вполне подойдет. Для остальных целей существует JMeter.

Итак, мы почти готовы начать. Осталось определиться с одной незначительной деталью. А что мы, собственно говоря, будем тестировать?

Описание примера

В примере мы подготовим наш собственный REST-сервис. У сервиса будет три ендпоинта. Первый всегда будет возвращать корректный результат. Второй будет возвращать ошибку в половине случаев (для демонстрационных целей мы будем выбрасывать ошибку намеренно). Третий - в трети случаев.

Для этого специально подготовленного сервиса мы создадим проект в SoapUI где реализуем функциональные тесты для нашего API, и в дальнейшем интегрируем их в нагрузочный сценарий.

Реализуем демонстрационный REST-сервис

Сервис мы напишем на языке Python с использованием библиотеки Flask. Этот выбор был сделан из-за простоты, быстроты и наглядности шагов, необходимых для реализации такого сервера. Однако, если вы никогда не работали с Python, то, скорее всего, вам придется выполнить нехитрые подготовительные процедуры:

  • Скачать и установить версию Python для вашей операционной системы. Если вы пользуетесь операционной системой Linux, то, скорее всего, у вас уже всё установлено. Проверить можно командой python. Если у вас Debian или Ubuntu, то вероятно, вам придется установить менеджер пакетов pip, при помощи команды sudo apt install python-pip

  • Установить Flask командой pip install -U flask

После того, как подготовительные процедуры завершены, мы можем приступить к написанию нашего тестового сервера. Ниже представлен код сервиса. Вам надо скопировать его к себе в файл на жёстком диске (например - demoservice.py).

from flask import Flask
from flask import Response
from random import choice

app = Flask(__name__)

luck = "You're lucky now.."
bad_luck = "Bad luck.."

@app.route('/')
def wec():
  return 'WebElement.click!'

@app.route('/half_error')
def half_error():
  if choice([True, False]):
    return Response(bad_luck, status=500)
  else:
    return Response(luck, status=200)

@app.route('/third_error')
def third_error():
  if choice([True, False, False]):
    return Response(bad_luck, status=500)
  else:
    return Response(luck, status=200)


if __name__ == '__main__':
  app.run(host='0.0.0.0', port='9090')

При условии что, ваш код был сохранен в файл demoservice.py, запустить наш тестовый сервис можно теперь командой python demoservice.py.

В принципе, код получился весьма самодокументированным. Поясню лишь некоторые моменты.

  • if choice([True, False]) и if choice([True, False, False]) - такая конструкция используется для случайной выборки из имеющегося списка. В первом случае, мы получим положительный результат с вероятностью 1/2, а во втором - с вероятностью 1/3.

  • app.run(host='0.0.0.0', port='9090') - означает что мы разрешаем обращения ото всех хостов сети, а также, что наш сервис будет висеть на порту 9090

Создаём проект в SoapUI

Перед началом описания проекта стоит отметить, что в моем случае я буду использовать утилиту SoapUI версии 5.5.0 для операционной системы Linux. Тем не менее пример будет валиден и для остальных сборок утилиты.

При создании и отладке проекта желательно иметь наш демонстрационный сервер запущенным.

Итак, открываем SoapUI и нажимаем кнопку "REST" ("Создать новый REST проект"). В поле URL указываем наш базовый ендпоинт и не забываем указать номер порта, на котором у нас всё будет запускаться: http://localhost:9090/.

Создав проект, переименуйте его во что-то осмысленное (например в WebElement.Click) и сохраните проект на жёстком диске. К этому моменту у нас появилось описание GET-запроса к нашему базовому адресу, который по задумке никогда не возвращает ошибку.

SoapUI Performance Empty Project

Если к этому моменту наш сервер запущен, мы можем выполнить запрос из SoapUI и убедиться, что мы получаем от него корректный отклик.

Добавляем два оставшихся ресурса. Кликаем правой кнопки мыши на узле сервиса (сразу под именем нашего проекта) и выбираем New Resource. В первом случае добавляем ресурс /half_error, а во втором - /third_error. Каждый такой ресурс содержит в себе по экземпляру GET-запросов, сгенерированных по умолчанию. Их мы будем использовать в дальнейшем. Проверяем работоспособность ручным запусков запросов (рекомендую проверять результат во вкладке raw - там будет видно и тело ответа и код ответа, который для второго и третьего ресурса иногда будет неуспешным).

Добавляем тестовые шаги

Перед тем как сформировать нагрузочный тест, нам необходимо описать функциональный сценарий, который мы в дальнейшем используем как базу для нагрузки. Выполняем следующую последовательность шагов:

  1. Кликаем правой кнопкой на узел проекта и выбираем New TestSuite. Указываем какое-то осмысленное имя для нашего тестового набора. Например - WebElement.Click Test Suite.

  2. Опять кликаем правой кнопкой, но уже только что созданный узел тестового набора и выбираем New TestCase. Устанавливаем имя узла в WEC Load Scenario.

  3. Раскройте созданный узел тест кейса. В нем должны находиться несколько категорий. В том числе и Test Steps. Перетащите мышкой сгенерированные реквесты в эту категорию один за другим. При перетаскивании SoapUI предложит вам дать имя создаваемым шагам. Рекомендую дать шагам некие осмысленные имена. Например шаг, запрашивающий ресурс / мог бы называться Always get OK, шаг для ресурса /half_error реквест мог бы называться Get OK at half time, а для ресурса /third_error шаг мог бы называться Get OK at two third times.

  4. На данном этапе наши шаги не проверяются на соответствие результата ожидаемому значению. Но если мы говорим о тесте, неотъемлемым атрибутом шага должна быть проверка результата. Поэтому, двойным щелчком по узлу шага мы открываем шаг на редактирование и ищем внизу открывшегося модального окна слово "Assertions". Если нажать на эту кнопку откроется секция настройки проверок результатов. Нажимаем "+", находящийся в верхней части открывшейся секции. В левой части диалога выбираем "Compliance, Stauts and Standards", а в правой выбираем "Valid HTTP status codes" и нажимаем "Add". В поле валидных статусов пишем "200" и нажимаем [OK]. Повторяем операцию для каждого шага нашего теста.

  5. [Опционально] Последним пунктом нам надо отсортировать шаги по вероятности возникновения ошибки (это опциональный шаг, который призван повысить репрезентативность результата). Дело в том, что каждый наш шаг подразумевает возникновение "ошибки" с демонстрационными целями. И каждый шаг выдает "ошибку" с разной вероятностью: первый - с нулевой, второй с половинной и третий с вероятностью одна третья. Кроме того, в том случае если SoapUI получает ошибочный ответ на один из шагов теста, то остальные шаги уже не выполняются, а тест помечается неуспешным. Итак, отсортируем (выбираем шаг и используем сочетание клавиш Ctrl+Up/Down для перемещения шага вверх либо вниз) шаги следующим образом:

    1. Always get OK

    2. Get OK at half time

    3. Get OK at two third times

На данном этапе у нас готов функциональный тест. Мы можем открыть окно запуска теста двойным щелчком по узлу "Test Steps" и попробовать позапускать его.

Создаем нагрузочный тест

Дело за малым. Осталось добавить экземпляр нагрузочного теста с настройками нагрузки, а также проверкой временных характеристик ответов от нашего сервиса. Щелкните правой кнопкой мыши на узле Load Tests и выберите New LoadTest. Дайте тесту какое-либо осмысленное имя, например Simple Load Test.

Вы увидите модальное окно настройки нагрузочного теста, которое одновременно является и окном, показывающим результаты теста. Порядок следования шагов будет соответствовать порядку указанном в узле Test Steps. При том, если вы поменяете порядок в окне нагрузочного теста, порядок поменяется везде.

SoapUI Performance Default Load Test

Оставим конфигурацию нагрузки, заданную по умолчанию (более подробно о поддерживаемых конфигурация можно прочитать в официальной документации SoapUI) и запустим наш тест. Обратите внимание на значение в графе cnt для первого шага. Оно говорит об общем количестве запусков, которые SoapUI произвел в соответствие с текущей конфигурацией нагрузочного теста. Первый шаг (ожидаемо) завершился без функциональных ошибок для всех запущенных экземпляров нашего теста, поэтому во втором шаге значение cnt осталось неизменным.

SoapUI Performance Default Load Test Executed

Также выглядят ожидаемыми значения в графе err для всех шагов. Для первого шага - это ноль, для второго - примерно половина от cnt первого/второго шагов, а для третьего - треть от cnt третьего шага.

Проверки количественных характеристик отклика

Те ошибки, которыми мы оперировали до сих пор относятся к разряду функциональных. Это означает, что в реальной среде на сервере бы возникла ошибка логики и он вернул бы что-то отличное от того, что мы считаем успешным ответом. В нагрузочных тестах SoapUI мы может также проверять то, насколько количество ошибок, время отклика и некоторые другие характеристики соответствуют ожидаемым значениям.

В окне конфигурации нагрузочного теста ищем внизу кнопку "LoadTest Assertions", нажимаем ее, а затем нажимаем зеленый "+" в верхней части открывшейся секции. Выбираем в появившемся окне "Max Errors" и жмем [OK].

Более подробно с возможностями проверок количественных характеристик ответов в нагрузочных тестах можно ознакомиться в официальной документации SoapUI. В нашем примере мы будем использовать метрику "Максимальное количество ошибок на шаге". Целевое значение для неё может устанавливаться как в абсолютном (количество ошибок), так и в относительном (доля ошибочных ответов в общем количестве запусков) формате. Для примера возьмем наш шаг который "падает" в половине случаев и укажем значение 0.5 (при достижении или превышении значения, тест будет считаться неуспешным).

Указываем значение 0.5 в графе Max Relative Errors и 2500 в графе Max Absolute Errors. В поле Test Step выбираем значение Get OK at half time. Сохраняем нашу проверку. Также для демонстрационных целей подправим немного конфигурацию запуска нашего нагрузочного сценария. В поле Limit укажем значение 5000, а в соседнем поле размерностей выберем Total Runs. Запустим и проверим что получилось.

SoapUI Performance Load Test Assertion Failed

Как мы можем убедиться, тест остановился не дойдя до конца, как только текущий показатель доли ошибочных ответов перевалил за половину от общего количества запущенных к данному моменту "вторых" шагов.

Итак, мы научились создавать, конфигурировать и запускать нагрузочные тесты в SoapUI. Кроме того, пример демонстрационного сервиса, написанного на языке Python также выглядит весьма полезным и может найти применение в решении повседневных тестовых задач (например таких как создание простейших заглушек в вашей тестовой среде). В статье я покрыл основную часть возможностей по написанию нагрузочных сценариев, не затронув тем не менее такие важные моменты как использование groovy-скриптов и описание различных видов поддерживаемых стратегий нагрузки. Всё это помогает сделать тесты более гибкими (особенно groovy-скрипты), но также делает статью информационно-перегруженной. Я коснусь этих моментов в одном из следующих постов.

Остались вопросы? Задавайте их тут. Я постараюсь дополнить статью опираясь на ваши замечания.