Thursday, December 22, 2011

Для чего нам нужен Foreach

Постановка задачи.
Ответ (response, он же респонс - мильпардон за слэнг) одного из запросов (request, он же реквест) возвращает несколько числовых значений одновременно, из которых потом нужно сформировать строку определённого вида ("[1,2,3,4,5]") и засунуть её в переменную для дальнейшего использования в качестве параметра в другом запросе.

Для описанного ниже регулярного выражения, запрос возвращал респонс, состоящий из нескольких элементов (с разными значениями, естественно).

"335817" : {
"name" : "Price",
"id" : 335817,
"type" : "price",
"source" : "UTP",
"mId" : 335816
}
На выходе мне нужно было получить строку, которая бы содержала все id в виде ['<'id1'>','<'id2'>',...,'<'idn'>']

Шаг первый - дёргаем группы.
Делается это элементарно. К первому респонсу (что возвращает, в том числе, и искомые числовые значения), добавляем как child (дочерний элемент) Regular Expression Extractor c такими параметрами:


Где
Name ставите произвольное.
Apply to - где будете искать. Мне нужно искать в основном семпле (не в дочерних запросах).
Response field to check - в каком месте запроса искать нужное. Мне нужно искать в самом теле запроса.
Reference Name - имя "переменной", по которой мы будем обращаться к наденному фрагменту(ам).
Regular Expression - само регулярное выражение. Я проверяю его с помощью очень удобного фришного инструмента - Rad Software Regular Expression Designer.
Template - какую порядковую группу из найденных мы используем (если ищем несколько, можем и искать несколько). У меня единственная, она же первая группа.
Match No - тут поступаем так:
Если нужно использовать любое из найденных, ставим 0.
Если нужно использовать какое-то n-ное вхождение, ставим целое положительное n. (Например, 1 будет нам находить первое вхождение из группы).
Чтобы выгрести все - используем отрицательное значение (именно поэтому у меня стоит -1).
Default Value - исходное значение, которое будет подставляться, если искомый фрагмент(ы) не найден.


Шаг второй. Используем ForEach контроллер.

После запроса, из которого мы дёрнули группу, добавляем ForEach контроллер с такими параметрами:

Где
Name ставите произвольное.
Input variable prefix - то самое Reference Name из предыдушего запроса, по которому мы будем обращаться к выделенной группе.
Output variable name - куда мы будем складывать полученное значение.


Шаг третий. Слепляем элементы группы в одну строчку.
К нашему Foreach контроллеру как дочерний элемент добавляем BeanShell Sampler, в коде которого мы пишем, как именно мы хотим видеть строчку.

Я написала так:
String y=vars.get("mIds");

if (y.length() != 0)
{
     y=y.substring(0,y.length()-2);
     y+=",";
}
else
{
     y+="[";
}

y+=String.valueOf("${
meterval}");
y+="]";
vars.put("
mIds",y);


Где переменная mIds была описана в User Defined Variables с пустым значением.

На выходе из ForEach мы получим переменную mIds, которую в дальнейшем можно использовать где угодно. Значением этой переменной и будет искомая строчка.


ПыСы. При отладке запроса, использующего нашу переменную (mIds), не забываем enabl'ить шаги, с помощью которых мы эту переменную получаем :))))
ПыПыСы. Программист из меня тот ещё... Только что дошло, как убрать 4й шаг :)
Потому апдейтим скрипт.

Friday, December 16, 2011

Как запомнить HTTP статусы :)

Не могла удержаться и не свистнуть вот отсюда.

Огромное спасибо автору GirlieMac, aka Tomomi Imura за позитив!





































Wednesday, December 14, 2011

Как подружить Jmeter и Jenkins?

У нас на проекте существует какая-никакая Continuous Integration (CI) с выделенным серваком и прикрученным к нему Дженкинсом (вариант Hudson - оба являются CI-серверами).

Постановка задачи: для начала прикрутить два несложных джиметровских скрипта с тем, чтобы можно было смотреть что-то типа aggregate report родного Джиметра. Имя хоста передаётся параметром.

Шаг 1. Установить на Jenkins Performance Plugin путём стандартных средств Дженкинса: Jenkins -> Manage Jenkins -> Manage Plugins -> Available. Найти нужный и нажать [Install].

Шаг 2. Заливаем на сервер джиметр. У меня на сервере живёт бубунта, и её стандартный package manager ставит джиметр версии 2.3. Потому я сверху установленного 2.3 стянула последнюю версию и распаковала её в другом месте _path_. Потом мы на это место будем ссылаться.

Шаг 3. Делаем job для каждого скрипта и запускаем его, дабы создались директории в /var/lib/hudson/jobs

Шаг4. Если мы задаём имя хоста в User Defined Variables, то там пишем следующее:
HOST (имя переменной) ${__P(server,_defaultHost_)} (значение переменной)
Где _defaultHost_ - имя хоста по умолчанию, а server - имя параметра, передаваемого в командной строке.
Такой скрипт можно гонять как из ГУИ, так и из командной строки.

Шаг 5. Для каждого job'a копируем в папку /var/lib/hudson/jobs/_JobName_/workspace/ необходимый параметризованный джиметровский скрипт и csv файлы. Допустим, у нас есть скрипт testjm.jmx.

Шаг 6. Настраиваем job'ы. (Configure)
Для этого открываем в Дженкинсе нужный Job (для каждого из двух скриптов был создан отдельный) и делаем следующее:

- Ставим галочку для "This build is parameterized" и пишем что-то типа
Name hostname
Default Value _servername_
Description Host name where the test will be run


- В разделе Build жмём кнопочку [Add build step] -> Execute shell.
В поле Command пишем:

echo $WORKSPACE
JMETER_HOME=/home/hudson/jakarta-jmeter-2.5/bin (_path_ куда был записан свежий джиметр)
PATH=$JMETER_HOME:$PATH
java -jar $JMETER_HOME/ApacheJMeter.jar -n -t $WORKSPACE/testjm.jmx -Jserver=${hostname} -l $WORKSPACE/jmeter.jtl


Этот скрипт будет выполнять наш скрипт testjm.jmx (-t) в командной строке (-n), передавая туда переменную для джиметра server, значение которой будет равняться параметру, заданному hostname.
Также в папку /var/lib/hudson/jobs/_JobName_/workspace/ будет положен лог джиметра с именем jmeter.jtl (-l).

- Ставим галочку Publish Performance test result report. Тут же нажимаем [Add a new report] и выбираем JMeter. В Report Files пишем имя нашего лога - jmeter.jtl

Шаг 7. Сохраняем.

Шаг 8. Бегаем.


Для того, чтобы после выполнения посмотреть лог, идём на

http://_server_/view/_path_/job/_JobName_/lastBuild/performance/

Тут _server_ - имя Дженкинс-сервера.
_path_ - путь до папочки "jobs".
_JobName_ - имя текущего job'a.

Перебираем все строки в csv файле.

За помощь в неравной борьюе большое спасибо Илье Пономаренко и Дэну Аркуше.

Постановка задачи: необходимо, чтобы цикл выполнялся ровно столько раз, сколько есть строк в csv-файле. Для каждого треда отдельно.
Т.е., если у нас есть
Готового решения в инете я не нашла (может, искала плохо, конечно).

Раньше проблема решалась таким образом: я создавала Loop Counter, задавала число проходов равным числу строк в csv, которое мне надо было пройти, и тогда всё работало замечательно.

Но проблема заключалась в том, что файл мог меняться, сответственно, могло меняться и количество строк в этом файле. Да и интересно было заставить джиметр автоматом считать, сколько раз нужно бегать.

Мучилась я долго, но придумала, наконец, нижеследующее.

Допустим, у меня есть csv файл с такими полями: userName,Pwd,grName. Точнее, файл содержит столбцы, чьи столбцы парсятся в переменные JMeter c именами userName,Pwd,grName.
В этом файле Х строк.

1. Редактируем файл, ставим в Х+1 строке слово FALSE в первом столбце (отвечающем за переменную userName). Сохраняем изменения.

2. Создаем Add -> Logic Controller -> While Controller, которому ставим значение для Condition (function or variable) равное ${username}

3. Добавляем в этот контроллер CSV Data Set Config, в котором прописываем нужный нам csv файл и все необходимые имена переменных из него.

4. Добавляем в контролер все шаги, которые необходимо выполнить в цикле.

Профит! :)

Единственный минус - цикл выполняется не Х, а Х+1 раз, и последний раз вместо значения переменной userName передаётся FALSE.

Но, как по мне, это лучше, чем править ручками.

Если кто знает лучший способ - буду рада, если поделитесь.

Tuesday, November 22, 2011

Перебираем циклом пользовательские переменные

Постановка задачи. Имеется N пользовательских переменных, значения которых вычисляются в BeanShall скрипте (в данном случае это - разные интервалы времени, но суть не важно). У этих пользовательских переменных user-friendly имена, так что сразу понятно, что передаётся этой переменной.
Необходимо выполнить некоторый однотипный набор действий, передавая параметрами все эти переменные.
То есть, нужно сделать цикл, который будет просто перебирать все эти переменные.

Первой мыслью было создать файл, куда запихиваются имена этих кастомных переменных с тем, чтобы потом их оттуда взять. Не сработало. Как бы ни мучались, имена передаются как текст.

Пришлось искать обходные пути.

Решение вышло несколько кривоватым, но, тем не менее, рабочим.

Итак в BeanShell скрипт и User Defined Variables добавляем T1..TN переменные, куда дублируем все нужные нам переменные с user-friendly именами.

Далее, создаем Loop, куда помещаем все нужные нам операции, которые должны быть выполнены в цикле. Ставим Loop Counter равным N (количество переменных).

Там, где нам нужно передавать значение переменной, в параметрах ставим
${__V(T${__counter(FALSE)})}
в нужном месте.

Всё работает :)

ПыСы. Отдельное спасибо Дэну Аркуше за подсказку решения "в лоб"

Дополнение. Нашлось лучшее решение.

Данный метод работает, если цикл у вас всего один. А что делать, если вложенных циклов несколько?

Параметр false у функции {__counter(FALSE)} означает, что этот счётчик будет увеличиваться только в пределах одного треда.
Но, если у вас будет сверху ещё 2 цикла с числом проходов X и Y, то счётчик ваш будет увеличиваться от 1 до X*Y*N, что приведёт к тому, что переменные у нас просто закончатся. нам-то нужно, чтобы этот счётчик работал от 1 до N.

В этом случае на помощь приходит конфигурационный элемент Counter


Задаём ему такие значения:
Start - 1
Increment - 1
Maximum - N (количество переменных)
Number format - 0 (в этом случает счёт начинается с 1, если Вы поставите 00, то счёт будет вестись с 01, если 000 - то с 001 соответственно).
Reference Name - Count (произвольное имя счётчика, на которое мы будем ссылаться).
Обязательно поставить галочку для "Track counter independently for each user".

После этого смело пишем этот счётчик везде, где он нам может понадобиться.

Т.е., ${__V(T${Count})}

Wednesday, November 9, 2011

Проверяйте системное время на виртуалках!!!

Два дня ковырялась, чего скрипт у меня генерит шопопало.
Время - 30е октября, хоть тресни.
Пока не дошло зайти и проверить системное время виртуалки (бубунта, штоп её).
И вы-таки не поверите...

Блондинко, блин...

Thursday, August 4, 2011

Добавляем кастомные функции в BeanShell

Исходя из предыдущего поста, возинкла необходимость написания простейшей функции, которая бы приписывала нолик к входному значению, в случае, если это значение меньше 10.
Два дня копала форумы, ничего не нашла.
А всего-то надо было почитать про BeanShell!!!!

Решение оказалось банальным до безобразия.

add( a, b )
{
return a + b;
}

foo = add(1, 2); // 3
foo = add("Oh", " baby"); // "Oh baby"

Итак, результирующий код выглядит следующим образом:

Calendar cal=Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"));

Conv(val)
{
String m = "";

if (val<10)

{
m = "0" + String.valueOf(val);
}

else
{
m =String.valueOf(val);
}

return m;
}

String newTime = Conv(cal.get(Calendar.YEAR)) + "-" + Conv(cal.get(Calendar.MONTH)+1) + "-" + Conv(cal.get(Calendar.DAY_OF_MONTH)) + "T" + Conv(cal.get(Calendar.HOUR_OF_DAY)) + "%3A" + Conv(cal.get(Calendar.MINUTE)) + "%3A" + Conv(cal.get(Calendar.SECOND)) + "Z";

vars.put("myTime", newTime);


Примечание! Если использовать Calendar cal=Calendar.getInstance() то у вас будет использоваться ваше _ЛОКАЛЬНОЕ_ время.
Мне это не подходило, т.к. объекты на сервере должны были создаваться по серверному времени.
Проблему решил перевод в формат UTC. Я использовала метод TimeZone.getTimeZone("America/Los_Angeles")) (список временных зон можно посмотреть вот здесь )
Однако, многим не нравится использование метода getTimeZone(), потому есть альтернативное решение.

Да, и ещё! Не забывайте о правильном использовании галочки "Encode?" в параметрах.
Если используются URL escape characters, то эту галочку ставить не нужно!