Чего нельзя сделать в Gatling

Не так давно потратил порядочное время, чтобы подходящий инструмент для нагрузочного тестирования в рабочем проекте. Прежде мы использовали JMeter, но с ним довольно тяжело писать кастомную логику, которая иногда бывает при проведении нагрузочных тестов (приготовить данные хитрым образом и тому подобное). С этой целью я и полез в интернеты за инфой. Мы отобрали несколько вариантов и в конце концов остановились на инструменте под названием Gatling. Вот его основные фичи:

  • Умеет стрелять по мишени
  • Строит подробные отчеты
  • Рисует красивые графики
  • Легко интегрируется в сборку
  • Достаточно богатый и годный DSL (хоть и на Scala)
  • Это всё-таки код (будет преимуществом для тех, кто устал программировать на XML)

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

P.S.: пост актуален на момент публикации, если вы его читаете через год, то помните, что что-то могло поменяться к этому времени

Нет зависимостей между сценариями

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

Как бороться

Не допускать таких сценариев, либо же объединять их в один. Также на форуме видел предложение попробовать разрулить такую ситуацию через различные способы синхронизации (семафоры и т.д.), но это уже откровенный костыль

after и before не работают с DSL

В Gatling DSL есть блоки before и after. Как вы наверное уже догадались, служат они для того, чтобы выполнить какие-то действия до начала тестов и после них. Ну, например, создать тестовые данные и удалить их после запуска. Так вот, если вы напишете в этих блоках exec( … ), то ничего не выполнится, никаких запросов не случится. Поначалу это кажется нелогичным, ведь почему бы не подготовить данные используя те же инструменты, что и для выполнения боевых стрельб. Но нет, тут как раз таки всё логично. Этот DSL заточен именно под запуск тестов, а не выполнение одиночных запросов. Такой язык описания наверняка не обойдется без костылей и будет сильно сложнее, чем сделанный под одну конкретную задачу

Как бороться

Притащить в проект отдельную библиотеку, которая будет выполнять всё что вам нужно.

Нет возможности получить количественные результаты выполнения

Когда вы пишете assert’ы, иногда бывает нужно вытащить количество успешно выполненных запросов. К примеру, чтоб проверить какие-то дополнительные параметры системы по окончанию тестов. Но получить количественные результаты не удастся, можно только сравнить их со значением извне

.assertions(details(paymentGroup).successfulRequests.count.is(totalCount))

сам count достать не получится. К счастью, это тоже довольно редкая ситуация

Как бороться

Попробовать посчитать то что вам нужно в самом начале или же в процессе выполнения.

DSL — это не код

Вот на это почему-то наступил я сам и несколько моих коллег, поэтому заострю ваше внимание. Нужно понимать, что через DSL вы просто описываете конфигурацию запуска, а не выполняете запросы. Чувствуете разницу? К чему это ведёт:

допустим вы хотите передать в заголовке уникальный id запроса, который генерируется случайным образом.

.headers(Map(
   "RequestId" -> RandomRequsetId.generate()
))

Так вот, в каждом запросе у вас будет один и тот же RequestId. Это происходит потому что вы делаете конфигурацию, а не выполняете этот код каждый раз.  RandomRequsetId.generate() вызовется ровно один раз и будет одинаковым для всех запросов. Такие дела

Как бороться

1. Можно использовать плейсхолдеры (работает для строк — каждый раз будет генерироваться новая строка):

.headers(Map(
   "RequestId" ->s"${RandomRequsetId.generate()"
))

2. Можно работать с сессиями. Выглядеть это будет примерно так:

.feed(requestIdFeeder)

Фидер генерирует значение (или читает его из файла, там множество реализаций) и кладет его в сессию. Затем можем пользоваться этим значением в самых разных местах.

.headers(Map(
   "RequestId" -> “${RequsetId}”
))

Довольно просто и удобно

Нет возможности сгруппировать результаты

Допустим у нас есть некая асинхронная обработка запроса. То есть мы отправили HTTP-запрос, система сказала что запрос принят и теперь мы ждём пока наш операция не завершится со статусом ОК. Мы просто периодически проверяем этот статус в блоке tryMax.

Но мы не можем сделать assert на время прохождения всей цепочки запросов от момента регистрации операции, до её непосредственного завершения. Мы можем только проверить длительность единичного запроса. Причём в таблицах отчета эту цифру таки можно увидеть, включив настройку gatling/charting/useGroupDurationMetric = true

Как бороться

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

Итого

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

Оставить комментарий

Ваш адрес email не будет опубликован.