вторник, 3 февраля 2015 г.

Работа с JSON REST Web services в JMeter

Очень многие Web и мобильные приложения взаимодействуют с сервером через API в стиле REST. Транспортным протоколом тут по прежнему является HTTP(S), но данные передаются в формате JSON (реже XML).
В этой статье рассмотрим как организовать нагрузочное тестирование такого рода сервисов с использованием JMeter. В качестве подопытного сервиса возьмем jsonplaceholder.typicode.com, который представляет собой типичный API для CRUD приложения типа блога или форума. Сервис позволяет получать, создавать, обновлять и удалять пользователей, некие посты этих пользователей, комментарии к постам плюс некоторые другие сущности.

Запрос на чтение данных и его парсинг.

Создать запрос на чтение списка, например пользователей, очень просто. Для этого стартовав с нашего базового шаблона добавляем GET HTTP Request к URL /users и после запуска тест-плана в качестве ответа видим список имеющихся пользователей в формате JSON.
Для данного простого сервиса не потребовалась даже настройка заголовков запроса с помощью HTTP Header Manager.
Далее предположим, что мы хотим получить все посты определенного пользователя, выбранного случайным образом. Для этого нам необходимо выделить id всех пользователей, содержащихся в ответе сервера и выбрать из них любой случайным образом. Простейшим методом сделать это является использование Regular Expression Extractor с соответствующими настройками: искать в body ответа последовательности вида "id": <целое число>,  выделять из них значения идентификаторов и из получившегося набора взять одно число случайным образом и поместить его в переменную USER_ID.
Добавив Debug PostProcessor чтобы увидеть значение переменных, запускаем, и видим, что id пользователя успешно выделяется и меняется при каждом новом запуске.
Однако для более сложных случаев разбор JSON с помощью регулярных выражений может оказаться достаточно сложным и неудобным. Поэтому по аналогии с XPath для XML придуман JSONPath и существует плагин JSON Path Extractor, который входит в набор плагинов Extras with Libs Set.
После установки плагина в списке элементов появляется JSON Path Extractor помощью которого становится можно получить из ответа сервера массив id пользователей с помощью лаконичного выражения JSONPath.
Правда JSON Path Extractor к сожалению не предоставляет возможности выбрать из результирующего массива случайный элемент. Для этого придется добавить дополнительную операцию, например все тот же Regular Expression Extractor, но на сей раз парсить мы будем уже не ответ сервера а значение переменной USER_ID_ARRAY, в которую сохранили массив.
Запустив тест-план, можем убедиться, что  массив id пользователей заполняется и в USER_ID из него выбирается случайный элемент.
Теперь осталось добавить еще один запрос с url /posts?userId=${USER_ID} и убедиться что мы получаем в ответе посты случайного пользователя.

Запросы на добавление и изменение данных

Создать запрос на добавление данных, например поста, несколько сложнее. Нужно создать POST HTTP Request  к url /posts, добавить элемент HTTP Header с хедером Content-type= application/json указывающиv на тип данных, которые мы будем передавать, и наконец поместить в тело запроса необходимые согласно описания API данные в формате JSON. Заметьте, что мы подставляем id случайного пользователя (полученный ранее) в качестве автора поста и активно используем встроенные функции JMeter для рандомизации данных.
Запускаем и убеждаемся что сервис вернул предусмотренный API ответ о успешном добавлении и присвоил нашему новому посту id 101.
Извлечем id добавленного поста из ответа при помощи JSON Path Extractor и сохраним в переменную. Теперь можно попробовать и провести и другие операции с нашим постом – изменить его и удалить.
Для изменения нужно создать PUT запрос на url /posts/${POST_ID} и в передаваемые JSON данные добавить id нашего поста.
Для удаления потребуется DELETE запрос с пустым телом.

Результирующий тест-план к данной статье можно скачать здесь.

Конечно, существуют и другие средства для тестирования REST API например SoapUI, но это уже совсем другая история.

среда, 21 января 2015 г.

Запись скриптов JMeter с HTTP(S) Test Script Recorder

Отлавливание нужных запросов с помощью таких средств как FireBug или Fiddler требует определенной сноровки. Да и в любом случае переносить их вручную в тест-план достаточно утомительно, особенно если запросов много и присутствуют POST-запросы для которых каждый параметр приходится переносить индивидуально.
К счастью JMeter предоставляет компонент HTTP(S) Test Script Recorder при помощи которого можно записать запросы, выполняемые в браузере. Этот компонент расположен в группе Non-Test Elements и добавить его можно только в WorkBench.
Script Recorder представляет из себя proxy-server, умеющий сохранять все проходящие через него запросы в указанный узел тест-плана.
В настройках Script Recorder нужно указать порт, на котором будет работать прокси (по умолчанию предлагается 8080) и выбрать узел тест-плана, в который будет сохраняться скрипт.
После этого можно запускать браузер. Его следует настроить на работу через прокси localhost на соответствующем порту.
Далее запускаем в Script Recorder запись кнопкой Start и выполняем в браузере желаемые действия. Ву-а-ля – записанные запросы появляются в JMeter.
Но в таком виде результат вряд ли Вас порадует, потому что большинство запросов браузера касаются картинок, js и css файлов и включать их в нагрузочный тест обычно нет никакой необходимости. В нашем примере я просмотрел всего две страницы на jmeter.apache.org, но получил «в нагрузку» десятки дополнительных запросов.  Чтобы избавиться от нежелательных запросов, следует задать в поле URL Patterns to Exсlude Script Recorder-а фильтр(ы) для отсева нежелательных запросов в виде регулярных выражений. Хорошей стартовой точкой является фильтр предлагаемый по умолчанию, добавить его можно кнопкой Add suggested Excludes.
В некоторых случаях предлагаемый фильтр не срабатывает, например если в URL запросов присутствуют параметры (а ля https://issues.apache.org/bugzilla/skins/standard/global.css?1369299041). В этом случае дефолтный RegEx придется подкорректировать, добавив в конец (\?.*)?  Кстати RegEx ожидаются python-style и должны соответствовать полному URL (включающему протокол, хост и параметры).
Но даже после этого остаются несколько ненужных запросов к доменам, посторонним для тестируемого сайта (в нашем примере это twitter). Чтобы их отсеять, нужно дополнительно добавить соответствующий RegEx фильтр и в поле URL Patterns to Include.
Теперь при следующей записи остаются только нужные запросы (последние два похожи, потому что это редирект).
Последний записанный запрос (серый) кстати использует протокол https – с ним Script Recorder также работает (в отличие от более старых версий JMeter), правда в браузере Вам придется согласиться на использование недоверенного сертификата, который автоматически создает JMeter. Чтобы заставить браузер доверять этому сертификату, а также в случаях когда JMeter не смог создать сертификат, придется обратиться к документации.
Наш записанный тест-план уже работоспособен и его можно запустить и посмотреть на результаты в View Results Tree.
Но успокаиваться ещё рановато, записанный тест-план практически всегда требует дополнительной доводки вручную. Первое что стоит сделать - удалить лишние HTTP Header Manager, которые создались для каждого запроса. Они необходимы только для Ajax запросов, которых в нашем случае нет. Можно конечно отключить запись HTTP-заголовков сняв опцию Capture HTTP Headers в рекордере но тогда для некоторых запросов возможно придется добавлять их вручную.
Также не помешает изменить имена записанных запросов на более понятные.
Кроме того рекордер помещает в поле Server Name or IP каждого записанного запроса имя хоста (сервера), что делает невозможным запуск записанного тест-плана на других серверах. Чтобы не заменять его везде вручную на имя пользовательской переменной, следует озаботиться, чтобы в перед началом записи в User Defined Variables была задана переменная со значением имени соответствующего хоста. В этом случае JMeter сам заменит при записи имя хоста на подстановку переменной.
У данной особенности есть и неприятных побочный эффект: на переменные заменяется всё что совпадает с их значениями. Например если у Вас есть переменная USER_COUNT со значением 1 то в сгенерированных самим же JMeter именах элементов все цифры 1 заменяться на ${USER_COUNT}. Так что на время записи возможно придется задизейблить основной User Defined Variables и вместо него временно создать упрощенный, чтобы избежать нежелательных эффектов.
Возможны и более сложные проблемы, требующие ручного вмешательства в записанный тест-план, например когда некоторые из записанных значений параметров запросов оказываются "одноразовыми" и при попытке воспроизведения запрос не проходит. Обычно такое случается с параметрами, обеспечивающими безопасность (security tokens), но это уже тема для отдельного разговора.

Результирующий тест-план к данной статье можно скачать здесь.

Существуют конечно же и альтернативные методы записи. Наиболее упоминаемым является BadBoy, который умеет сохранять захваченные запросы в формат JMeter.