Использование Gatling. Разбираемся в тестировании HTTP

Всем привет! Это команда тестирования производительности Тинькофф, и мы продолжаем цикл статей о Gatling. 

В предыдущей статье мы рассмотрели базовые возможности инструмента Gatling, узнали, как быстро создать шаблон gatling-проекта, и познакомились с новыми функциями библиотеки gatling-picatinny. Сегодня расскажем, как при помощи этих инструментов создать проект для тестирования HTTP-протокола.

Дисклеймер

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

Что такое HTTP-протокол

HyperText Transfer Protocol — протокол прикладного уровня передачи данных. Изначально — в виде гипертекстовых документов в формате HTML. Сейчас протокол используют для передачи произвольных данных. 

Основа HTTP 1.1 — технология «клиент — сервер». В этой схеме есть:

  • потребители (клиенты), которые инициируют соединение и посылают запрос;

  • поставщики (серверы), которые ожидают соединения для получения запроса, производят необходимые действия и возвращают обратно сообщение с результатом.

Gatling также имеет поддержку HTTP/2, но она пока экспериментальная. Как только добавят полноценную поддержку, мы обязательно напишем про это статью.

Как разработать скрипт для тестирования

В качестве тестового сервиса будем использовать сайт Computer Database. Пожалуйста, не подавайте на него нагрузку — используйте сайт только для отладки ваших скриптов.

Для скриптов реализуем такие запросы: вход на главную страницу и создание нового компьютера. Мы не будем разрабатывать проект с нуля, а используем уже готовый шаблон и с его помощью создадим проект ​​myhttpservice. Процесс создания мы описывали в предыдущей статье. А теперь разберемся, как создать нужный нам скрипт для тестирования HTTP-протокола.

Шаг 1. Обновляем зависимости

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

В файле project/Dependencies.scala проверьте актуальную версию Gatling и версию gatlingPicatinny.

 lazy val gatling: Seq[ModuleID]          = Seq(     "io.gatling.highcharts" % "gatling-charts-highcharts",     "io.gatling"            % "gatling-test-framework",   ).map(_ % "[current_version]" % Test)    lazy val gatlingPicatinny: Seq[ModuleID] = Seq(     "ru.tinkoff" %% "gatling-picatinny",   ).map(_ % "[current_version]")

В файле build.sbt проверьте, что версия scala поддерживается текущей версией Gatling.

scalaVersion := "[support_version]"

Загрузите новые зависимости в проект, запустив команду в консоли. Готово — вы обновили зависимости.

sbt update

Шаг 2. Меняем переменные

В файле src/test/resources/simulation.conf хранятся дефолтные переменные для запуска. Для нашего сервиса нужно обновить только baseUrl.

baseUrl: "http://computer-database.gatling.io"

Шаг 3. Создаем Action для публикации сообщений

В директории сases создайте новый файл для объекта HttpActions. В нем опишите все необходимые действия. Например, давайте создадим два запроса: get и post. Добавим к запросам параметры, тело запроса и проверки. Проверки помогут понять, успешно ли завершился запрос. Для большего разнообразия параметризуем запрос createNewComputer. Параметры «${randomComputerName}», «#{introduced}», «#{discontinued}» позволят использовать значения, полученные из Feeder. Параметр «#{company}» мы получаем из ответа на предыдущий запрос pressButtonAddNewComputer.

package ru.tinkoff.load.myhttpservice.cases  import io.gatling.core.Predef. import io.gatling.http.Predef. import io.gatling.http.request.builder.HttpRequestBuilder  object Actions {   val openMainPage: HttpRequestBuilder =    // Указываем имя запроса    http("Open main page")      // Указываем тип запроса (метод) и эндпоинт      .get("/")      .check(        // Проверяем, что в ответе пришел ОК        // (по умолчанию Gatling проверяет ответы на все успешные коды возврата: 2xx, 3xx)        status is 200,      )   val pressButtonAddNewComputer: HttpRequestBuilder =    http("pressButtonAddNewComputer")      .get("/computers/new")      .check(        status is 200,      )      .check(        // Забираем из ответа случайную компанию и сохраняем значение         // в переменной 'company'        regex("<option value=\"(.+?)\"").findRandom.saveAs("company"),      )   val createNewComputer: HttpRequestBuilder =    http("createNewComputer")      .post("/computers")       // Указываем параметры запроса. Обратите внимание на значение,        // таким образом мы можем параметризовать запросы.      .formParam("name", "#{randomComputerName}")      .formParam("introduced", "#{introduced}")      .formParam("discontinued", "#{discontinued}")      // Тут вместо #{company} подставится значение из предыдущего запроса                                           .formParam("company", "#{company}") }

Шаг 4. Используем Feeders

Узнать о Feeders побольше можно из предыдущей статьи. В нашем примере мы будем использовать фидеры из подключаемой библиотеки gatling-picatinny. Для этого в директории myhttpservice создайте новую директорию feeders, а в ней — object Feeders.

package ru.tinkoff.load.myhttpservice.feeders  import io.gatling.core.feeder._ import ru.tinkoff.gatling.feeders._  object Feeders {    // Для имени компьютера будем использовать случайную строку с нужным алфавитом.   val randomComputerName: Feeder[String] =     RandomRangeStringFeeder("randomComputerName", alphabet = ('A' to 'Z').mkString)    // Используем фидер для создания случайной даты   val introducedDate: Feeder[String] = RandomDateFeeder("introduced")    // Создаем случайную дату со сдвигом относительно 'introduced' даты   val discontinuedDate: Feeder[String] = RandomDateRangeFeeder("introduced", "discontinued", 3)    // Объединяем фидеры в одну переменную для удобства                                                     val feeders: Feeder[Any] = randomComputerName ** introducedDate ** discontinuedDate  }

Шаг 5. Пишем сценарий теста

Для этого измените код в CommonScenario. Опишите класс CommonScenario, в котором создаете сценарий — порядок выполнения определенных действий.

package ru.tinkoff.load.myhttpservice.scenarios  import io.gatling.core.Predef._ import io.gatling.core.structure.ScenarioBuilder import ru.tinkoff.load.myhttpservice.cases.Actions._ import ru.tinkoff.load.myhttpservice.feeders.Feeders._  /* Объект-компаньон для класса CommonScenario, по сути синтаксический сахар, чтобы можно было вызвать сценарий таким образом CommonScenario(), вместо new CommonScenario().scn */ object CommonScenario {   def apply(): ScenarioBuilder = new CommonScenario().scn }  class CommonScenario {    // Создаем сценарий и его имя   val scn: ScenarioBuilder = scenario("CommonScenario")     // Подключаем наши фидеры     .feed(feeders)     // Подключаем наши запросы     .exec(pressButtonAddNewComputer)     .exec(createNewComputer) }

Шаг 6. Создаем описание HTTP-протокола

По умолчанию в проекте из шаблона g8 уже настроен протокол HTTP. Для нашего скрипта в файле myhttpserviсe.scala опишем еще один протокол. 

package ru.tinkoff.load  import io.gatling.core.Predef._ import io.gatling.http.Predef._ import io.gatling.http.protocol.HttpProtocolBuilder // Подключаем стандартные переменные из gatling picatinny import ru.tinkoff.gatling.config.SimulationConfig._  package object myhttpservice {    val httpProtocol: HttpProtocolBuilder = http     // Используем стандартную переменную из gatling picatinny, значение которой подтянется из simulation.conf     .baseUrl(baseUrl)     // Базовые заголовки. Для большинства кейсов этого будет достаточно и ничего менять не нужно.                          .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")     .acceptEncodingHeader("gzip, deflate")     .acceptLanguageHeader("en-US,en;q=0.5")     .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0")     // Если требуется добавить авторизацию, можно использовать специальные методы.     .authorizationHeader("Bearer token")     // Или определить свой заголовок     .header("customHeader", "value") }

Шаг 7. Подготавливаем нагрузочные тесты

В файле Debug.scala раскомментируйте строку с proxy для локальной отладки. Перед заливкой в VCS нужно либо убрать proxy, либо закомментировать его. 

package ru.tinkoff.load.myhttpservice  import io.gatling.core.Predef._ import io.gatling.http.Predef._ import ru.tinkoff.gatling.config.SimulationConfig._ import ru.tinkoff.load.myhttpservice.scenarios.CommonScenario  class Debug extends Simulation {    setUp(     // Запускаем наш сценарий     CommonScenario()       // Запускать будет один пользователь - одну итерацию       .inject(atOnceUsers(1)),   ).protocols(     // Работа будет проходить по протоколу, который описан в конфигурации httpProtocol     httpProtocol       // Настраиваем прокси для отладки запросов, используем для этого fiddler        // или charles (8888 стандартный порт для прокси)       .proxy(Proxy("localhost", 8888).httpsPort(8888))                                                   )     // Максимальное время теста равно testDuration, если тест не завершится за меньшее время,      // он будет остановлен автоматически     .maxDuration(testDuration) }

Для MaxPerformance- и Stability-тестов ничего менять не нужно. Подойдут дефолтные настройки для HTTP.

Шаг 8. Запускаем Debug-тест

Для запуска используйте GatlingRunner. После выполнения скрипта можно посмотреть консоль fiddler или charles и увидеть наши запросы.

package ru.tinkoff.load.myhttpservice  import io.gatling.app.Gatling import io.gatling.core.config.GatlingPropertiesBuilder  object GatlingRunner {    def main(args: Array[String]): Unit = {      // Указывает имя симуляции Debug, либо какой-то другой,      // например, MaxPerformance     val simulationClass = classOf[Debug].getName      val props = new GatlingPropertiesBuilder     props.simulationClass(simulationClass)      Gatling.fromMap(props.build)   }  }

Так, например, выглядят наши запросы в charles.

Также скрипт можно запустить из консоли командой.

sbt "Gatling / testOnly ru.tinkoff.load.myhttpservice.Debug"

Ниже — пример успешного запуска скрипта.

Simulation ru.tinkoff.load.myhttpservice.Debug started...                               ================================================================================ 2022-01-27 11:44:22                                           0s elapsed ---- Requests ------------------------------------------------------------------ > Global                                                   (OK=3      KO=0     ) > pressButtonAddNewComputer                                (OK=1      KO=0     ) > createNewComputer                                        (OK=1      KO=0     ) > createNewComputer Redirect 1                             (OK=1      KO=0     )  ---- CommonScenario ------------------------------------------------------------ [##########################################################################]100%           waiting: 0      / active: 0      / done: 1      ================================================================================  Simulation ru.tinkoff.load.myhttpservice.Debug completed in 0 seconds Parsing log file(s)... Parsing log file(s) done Generating reports...  ================================================================================ ---- Global Information -------------------------------------------------------- > request count                                          3 (OK=3      KO=0     ) > min response time                                    153 (OK=153    KO=-     ) > max response time                                    319 (OK=319    KO=-     ) > mean response time                                   209 (OK=209    KO=-     ) > std deviation                                         78 (OK=78     KO=-     ) > response time 50th percentile                        154 (OK=154    KO=-     ) > response time 75th percentile                        237 (OK=237    KO=-     ) > response time 95th percentile                        303 (OK=303    KO=-     ) > response time 99th percentile                        316 (OK=316    KO=-     ) > mean requests/sec                                      3 (OK=3      KO=-     ) ---- Response Time Distribution ------------------------------------------------ > t < 800 ms                                             3 (100%) > 800 ms < t < 1200 ms                                   0 (  0%) > t > 1200 ms                                            0 (  0%) > failed                                                 0 (  0%) ================================================================================

Заключение

Мы рассказали, как быстро и легко создать проект из нашего шаблона и написать скрипты для HTTP-протокола. В этом нам помогла наша библиотека Picatinny.

В следующих статьях поговорим о том, как написать скрипты для протоколов gRPC,  AMQP, JDBC и kafka.

Полезные ссылки

  1. Подробнее о сессии в Gatling.

  2. Подробнее о составлении HTTP-запросов в Gatling.

  3. Подробнее о фидерах в Gatling.

  4. Подробнее о настройке протокола HTTP в Gatling.

  5. Проект Gatling из примеров этой статьи.


ссылка на оригинал статьи https://habr.com/ru/company/tinkoff/blog/658479/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *