Тестируй веб-сайты вместе с Jmeter

June 12, 2008

Это статья является логическим продолжением серии материалов под названием “Тестируй веб-сайты вместе с Badboy”. Как видите, названия статей похожи, отличия только в конкретных программных продуктах, которые будут использованы. Сегодня я расскажу, что такое Jmeter, как он соотносится с badboy.

Jmeter - один из известнейших и старейших продуктов (домашний сайт проекта http://jakarta.apache.org/jmeter/). Jmeter написан на java (значит, для работы jmeter вам потребуется установленная JRE) и доступен в виде открытых исходных кодов. На сегменте рынка посвященному тестированию веб-приложений существует множество программных продуктов решающих схожие задачи, но чуть-чуть с разных сторон. Jmeter не очень подходит на роль средства для тестирования корректности работы интерфейса (вспомните о режиме navigate для badboy), а вот как средство для генерации трафика, имитации поступающих на сайт запросов клиентов с последующим анализом и визуализацией – Jmeter вне конкуренции. Изначально Jmeter создавался как инструмент для тестирования веб-приложений, однако затем его функции были расширены, и вы можете дополнительно тестировать native java-приложения (работа с базами данных, интеграция с JUnit, SOAP, LDAP, работа с почтой). В отличие от badboy, Jmeter не похож на “большой магнитофон с двумя кнопками”: действия по созданию сценария тестирования требуют большей ручной работы, но зато и получаемый результат выше. Для самого первого знакомства с Jmeter может подойти такой сценарий, когда вы записываете в badboy сценарий тестирования, а затем экспортируете его в Jmeter. Однако не надейтесь, что перенос будет выполнен без ошибок: есть достаточное количество элементов сценария badboy, которые не переносятся в Jmeter. Прежде всего, это действия, записанные в режиме Navigate, затем работа с источниками данных (загрузка данных из excel-файлов), работа с расписанием, не поддерживается объект “Thread Control” (напоминаю, что “Thread Control” служит для запуска параллельных копий потоков и имитации нагрузки на сайт). Одной из важных функций в тестировании производительности сайта с помощью Jmeter является средства для распределенного управления (когда на нескольких машинах запущен Jmeter, генерирующий трафик, но управление этими копиями Jmeter выполняется с одного центрального компьютера).



Ключевое понятие Jmeter – план тестирования (обратите внимание на рис. 1, что дерево элементов изначально состоит из двух пунктов “Test Plan” и “Workbench”). Workbench – это … что-то похожее на временную папку. Дело в том, что интерфейс Jmeter построен на “перетаскивании” с места на место элементов дерева сценария (настоящее дерево плана, выполняемое, это как раз “Test Plan”). Вы конструируете дерево плана, а затем может его части перемещать на “верстак” (Workbench) и обратно. Выполняется в ходе тестирование только содержимое Test Plan, а содержимое Workbench даже не сохраняется на диск. Для наполнения Test Plan элементами сценария используйте либо через контекстное меню на элементе “Test Plan”, либо главное меню приложения “Edit -> Add -> Элемент, помещаемый в сценарий”.

Все добавляемые элементы делятся на категории: “Logic Controller” (служит для управления ходом выполнения сценария, содержит аналоги циклов, условных операторов). Следующая категория элементов “Listener”, назначение этого элемента - анализ выполнения сценария, например, построение графика или таблицы с результатами тестов. Категория элементов “Sampler” служит для выполнения, собственно, запросов (http, ftp, обращения к базам данных и т.д.). Категория “Assertions” служит для проверок того, что сформированная сервером страница соответствует некоторому критерию “правильности” (например, наличие в тексте страницы определенных фраз или html-элементов). Элементы в группе “Timers” служат для генерации пауз (а бывают они не только простыми, например, всегда одинаковая пауза в 0.5 секунды, но и распределение Гаусса или равномерное) между отдельными шагами (Samplers) внутри Thread Group (о ней чуть позже). Если вы не укажите величину паузы, в этом случае шаги сценария будут выполняться друг за другом без задержек, а значит генерируемая одним потоком (Thread) на сайт нагрузка будет превышать нагрузку, которую мог бы создать обычный посетитель (не робот). Следующей группой элементов сценария является “Config Element”, их задача создать типовые настройки для Sampler-ов. Группы Pre-Processor и Post-Processor содержат элементы, которые выполняются до выполнения Sampler-ов (например, модификация параметров запроса перед его отправкой) и после того как Sampler отработал (извлечь из результата запроса какие-нибудь переменные).

Итак, типовая заготовка плана тестирования состоит из Thread Group, HTTP Request Defaults, HTTP Request, Response Assertion ,Graph Results. Самым главным является элемент Thread Group (он не вложен в какую-либо из перечисленных выше групп элементов и существует как бы над ними всеми). Thread Group позволяет задать параметры генерируемой на сайт нагрузки. Его параметрами являются: во-первых, “Number of threads” (количество имитируемых пользователей одновременно работающих с сайтом), затем “Ramp-up period” (промежуток времени, через который выполняется запуск очередного процесса). Для того чтобы указать сколько раз будет выполняться сценарий внутри “Thread Group” используется “Loop count” (возможно, либо указать некоторое число, либо поставить галочку “Forever”, так чтобы сценарий выполнялся до тех пор, пока не будет прерван вами явно). У многих элементов (не только Thread Group) есть опция Name (имя элемента). Названия, которые вы здесь дадите, появятся и в дереве плана тестирования, так что не забывайте давать хорошие имена элементам плана. Так же как в badboy, нам мало написать сценарий с http-запросов - нам нужно контролировать правильность выполнения каждого шага. Так вот, последний параметр настройки для “Thread Group” – действия, выполняемые после ошибки (несоответствия, обнаруженного с помощью Assertion). Возможные значения: либо продолжить выполнение теста, либо прервать один поток (тот самый в котором возникла ошибка), либо прервать выполнение всего плана тестирования. Затем внутрь “Thread Group” добавляются элементы запроса к web-страницам (за это отвечают “HTTP Request Defaults” и “HTTP Request”). Первый из них служит для настройки типового запроса, второй выполняет сам запрос. Действительно, если мы тестируем один и тот же сайт, то такие параметры как протокол, имя сервера, номер порта и каталога с тестируемыми страницами будет совпадать (вероятно, частично совпадут и переменные, например всем страницам сайта может передаваться номер сессии). Мы можем указать все общие свойства для запросов, равно как и признак загружать с сайта не только html-код страницы, но и все связанные ресурсы (картинки, css) с помощью элемента “HTTP Request Defaults”. В последующих элементах “HTTP Request” указываются только параметры, отличные от указанных в настройках Defaults-а. Последний элемент сценария – Graph Results, назначение этого элемента построить график на основании данных поступающих в ходе тестирования. Теперь разберем каждый из элементов сценария подробнее. Начнем с изучения видов Controller-ов, попробуем создать цикл или условие.

Прежде всего нам нужен способ удобной организации выполняемых шагов сценария в виде иерархии “набор тестов->тест->шаг …”. В Jmeter для этого используется “Simple Controller” (для этого элемента нет никаких настроек кроме названия), визуально этот элемент будет представлен в виде “папки” в которую помещаются другие элементы (см. рис. 1, дерево сценария). Остальные контроллеры не столь тривиальны, и чтобы у вас не возник вопрос, зачем нужно такое разнообразие, я приведу пример небольшого плана теста, а затем мы подумаем, как же его реализовать. Предположим, что мы делаем сайт, где хранится набор видео-файлов в формате flash video. Пользователи могут загружать на сайт видео в форматах отличных от flash, например, avi. Процедура конвертации запускается как только файл был загружен и занимает некоторое время, создает значительную нагрузку на процессор. Одним из ключевых требований к сайту является то, что во время этого конвертирования сайт должен быть доступен для других пользователей (можно ввести требование, чтобы любое действие выполнялось не более 3 секунд). Мы спрашиваем: сколько пользователей могут работать без дискомфорта во время загрузки и конвертации одного видео-файла, а если файлов будет два, три … Фактически, нам нужно построить график, где по оси OX будут расположено количество одновременно конвертируемых видео файлов, по оси OY будут расположено количество пользователей просматривающих видео, ходящих по страницам сайта … Представили? А теперь вопрос, как сделать такой тест в том же badboy? Боюсь, что простого пути (без написания головоломных сценариев на badboy javascript) нет. А вот Jmeter содержит набор контроллеров, позволяющих создать и цикл на некоторое количество повторений, и опциональный выбор одного из набора возможных действий (имитация “хаотичных” действий пользователя и т.д.). Наиболее часто используемый контроллер - Loop Controller, служит для организации цикла (его поведение похоже на Thread Group, но действия выполняются последовательно, а не параллельно). В качестве единственной опции настроек Loop Controller выступает число повторений цикла. Совместно с циклами используется контроллер Once Only Controller. Его назначение выполнить вложенные действия только один раз, не зависимо от того, сколько действий диктуется родительским контроллером. Например, если поместить Once Only Controller внутрь Loop Controller, то действие внутри Once Only будет выполнено всего один раз. Удобно использовать Once Only Controller и внутри Thread Group, например, процедура авторизации будет единой, а последующие действия будут выполняться каждым из потоков многократно. Еще сценарий: предположим, что мы создали цикл на три повторения, внутри которого разместили три действия (обозначим их как A, B, C). В этом случае действия будут выполняться следующим образом: ABC, ABC, ABC. Однако, мы хотим, чтобы внутри цикла выполнялся только один из трех возможных шагов (т.е. выполняемые действия чередуются: A, B, C). В этом случае нам поможет Interleave Controller (поместите его внутрь цикла, а внутрь самого Interleave Controller положите действия A,B,C). Для имитации случайного поведения пользователя (выбирающего на каждом из шагов теста одно из возможных действий) более подойдет такой контроллер как Random Controller. Он похож на Interleave Controller, только на каждой из итераций цикла выбирается одно из вложенных действий не последовательно, а случайно (в нашем примере с буквами это будет что-то вроде: A, B, A, C, A). Это еще не все: можно сделать так чтобы действия, помещенные внутрь контроллера выполнялись не всегда, а с некоторой долей вероятности (например, в 30% случаев). Такое поведение позволяет создать Throughput Controller. Для того чтобы протестировать код, который, возможно, приведет к timeout-у загрузки страницы (страница загружается слишком долго), используйте Runtime Controller (его единственный параметр - предельное время выполнения вложенных в этот контроллер действий). Еще один контроллер – IF Controller - позволяет проверить некоторое условие и выполнить вложенные внутрь себя действия только если условие выполняется. Например, работа с сайтом возможна только в случае, если процедура аутентификации прошла успешно. В качестве единственного параметра для IF Controller-а задается строка условия. В ней мы можем использовать переменные, например, так “${varname}” (стиль именования переменных такой же, как в badboy – и это удобно). Для создания переменных вы можете либо выделить элемент “Test Plan” и в окне его свойств в графе “User Defined Variables” можно установить значения переменных. Переменные можно назначить и вызвав меню “Add -> Config Element -> User Defined Variables”. Обратите внимание, что IF Controller не содержит ветви else (действий выполняемых, если условие не выполнилось). Если же вам необходимо выполнить одно из нескольких действий, то советую обратить внимание на Switch Controller. Loop Controller не единственный способ организации цикла, есть еще и ForEach Controller. Его особенность в том, что цикл организуется не фиксированное количество раз, а динамически, на основании списка переменных, имена которых строятся по правилу “базовое имя_номер”. Например, если у вас есть переменные с именами var_1, var_2, … то они подходят для ForEach-цикла (столь странное требование к именованию переменных на самом деле не представляет больших трудностей, т.к. многие элементы Jmeter формируют результаты именно в таком стиле).

Итак, с помощью различного вида Controller-ов вы составили “скелет” плана тестирования, затем вы начинаете “наращивать мясо” с помощью различных Sampler-ов. В списке доступных: FTP Request, он служит для обращения к ftp-серверу, с тем чтобы загрузить с него некоторый файл. Элемент Http Request служит для выполнения запроса к веб-странице, помимо обычных текстовых переменных можно на вход скрипту подать файл (но только один, к сожалению). Элемент JDBC Request позволяет выполнить обращение к серверу базы данных, и выполнить какой-либо из запросов. Элемент Java Request является универсальным элементом для запуска произвольного java-кода (единственное требование, чтобы тестируемый класс реализовал интерфейс JavaSamplerClient). В окне настроек вы указываете имя класса и набор пользовательских переменных, которые будут переданы классу перед началом его тестирования. Обнаружение классов доступных для запуска выполняется автоматически: Jmeter сканирует classpath в поиске всех классов поддерживающих интерфейс JavaSamplerClient и добавляет их в этот список. Для настройки classpath перейдите в дереве проекта на элемент Test Plan и в окне свойств обратите внимание на графу “Add directory or jar to classpath”. Java Request совместно с JDBC Request являются очень неплохим средством для тестирования производительности отдельных частей сайта (логики доступа к данным). Если ваше приложение построено на базе веб-сервисов, то тестирование пройдет легче, если воспользуетесь WebService Request. Для теста производительности почтового сервера используйте Mail Reader Sampler.Список доступных для использования Sampler-ов очень широк, но я упомяну еще только об одном из них – BeanShell Sampler. Т.е. с помощью этого Sampler-а вы можете выполнить как элемент плана тестирования произвольный код (фактически это “точка расширения” Jmeter, если вам не хватает его стандартных функций). BeanShell - один из многих (зато исторически самый первый) скриптовых языков для java. Любому “большому” приложению нужен внутренний язык (как visual basic в ms office). И хотя современные версии java уже имеют встроенную поддержку скритовых языков, но раз BeanShell был первым, то успел завоевать популярность (домашний сайт проекта http://www.beanshell.org/). Синтаксис BeanShell похож на javascript (значит, не слишком сложный), но главное то, что из BeanShell можно получить доступ к специальным переменным объектам самого Jmeter, изящно управлять поведением сценария.

После каждого из Sampler-ов следует разместить код проверки правильности выполненного запроса - Assertion. Самый простой вид проверки – Response Assertion, проверит наличие на странице некоторого текста (поиск выполняется на основании регулярного выражения). Если вернуться к примеру с верхней границей на время выполнения сайтом работы, то пригодится Duration Assertion (его единственный параметр – число миллисекунд, отведенное на успешное завершение операции). Еще к разряду простых проверок относится Size Assertion (проверка того что сформированная страница/файл имеют определенный размер). Для проверки того, что сформированный документ является корректным XML-документом или содержит элемент, расположенный по заданному Xpath-пути используются XML Assertion и XPath Assertion. Если проверка, которую нужно выполнить, сложна и не предусмотрена как стандартная функция JMeter, то используйте BeanShell Assertion. Завершив планирование теста, не забудьте поместить в его конец элементы для визуализации хода тестирования в виде графика или таблицы (Graph results, View results in table).

Теперь вы готовы запустить план на выполнение с помощью меню “Run -> Start”. Для того чтобы аварийно прервать работу скрипта используйте меню “Run -> Stop”. В ходе нескольких последовательных запусков в объектах Listeners накапливается статистика выполнения теста. Для того чтобы избавиться от лишней статистики используйте пункт меню “Run -> Clear All”. На этом все, введение в Jmeter получилось хоть и “по верхам”, но надеюсь, что оно даст вам точку отсчета. Последняя рекомендация: перед составлением плана тестировании в Jmeter, четко сформулируйте какие характеристики сайта вы хотите измерить, что влияет на эти характеристики, какие значения являются допустимыми.