Блок Gatling exec и переменные сеанса

У меня есть несколько симуляций Gatling, которые я пытаюсь структурировать таким образом, чтобы повторно использовать низкоуровневые вызовы (например, HTTP-вызовы) при построении различных сценариев. По этой причине у меня есть следующая структура (функция эха используется для объяснения текущего состояния):

// File UserAction.scala contains all low-level actions like load a specific page, login, logout etc. All functions return HttpRequestBuilder so that each simulation can perform its own set of checks on the responses.
object UserAction {
    ...
    def echo(): HttpRequestBuilder = {
        http("Postman Echo Service Call")
            .get("https://postman-echo.com/get")
            .headers(headers("headers_0"))
    }
    ...
}

// File TestSimulation.scala
class TestSimulation extends ParameterizedSimulation {
    private val accountFeeder = csv("data/accounts.csv")

    private val testScenario = scenario("Test Simulation")
        .feed(accountFeeder)
        .exec { session =>
            session.set("attributes", session.attributes.filterKeys(key => key.startsWith("some_prefix_")))
        }
        .doIfOrElse(session => session("schema_attributes").as[Map[String, String]].isEmpty) {
            exec(
                echo()
                    .check(bodyString.saveAs("responseBody"))
            )
        } {
            exec(
                echo()
                    .formParamMap("${schema_attributes}")
                    .check(bodyString.saveAs("responseBody"))
            )
        }
        .exec { session => println(session("responseBody").as[String]); session }
    ...
}

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

Когда я пытаюсь передать карту в качестве параметра функции echo (), возникают проблемы. Предположим, у меня есть следующая реализация эха:

def echo(extraParameters: Map[String, Any]): HttpRequestBuilder = {
    val request = http("Postman Echo Service Call")
        .get("https://postman-echo.com/get")
        .headers(headers("headers_0"))

    if (extraParameters.nonEmpty) {
        request.formParamMap(extraParameters)
    }

    request
}

и что симуляция делает следующее:

.exec { session =>
            session.set("schema_attributes", session.attributes.filterKeys(key => key.startsWith("some_prefix_")))
}
.exec { session =>
    echo(session.attributes.filterKeys(key => key.startsWith("some_prefix_")))
        .check(bodyString.saveAs("responseBody"))
    session
}
.exec { session => println(session("responseBody").as[String]); session }

Запросы не срабатывают, и я получаю следующую ошибку о том, что bodyString отсутствует.

[ERROR] i.g.c.a.b.SessionHookBuilder$$anon$1 - 'hook-1' crashed with 'j.u.NoSuchElementException: No attribute named 'responseBody' is defined', forwarding to the next one

Альтернативой, которую я пробовал, было заставить echo принимать Expression[Map[String, Any]] и использовать обычный блок exec (т.е. не тот, у которого был введен сеанс). В этом случае я не мог получить фактическую карту из сеанса!

Кажется, что bodyString доступна только при использовании exec (), а не exec {session => ...; session} форма. У меня очень мало знаний о Scala, что только усугубляет мое замешательство.

Я чувствую, что упускаю в этом что-то очень важное. Любая помощь будет принята с благодарностью.

ОБНОВЛЕНИЕ №1

На основании полученных комментариев, насколько я понимаю, использование функции exec(sessionFunction: Expression[Session]) создает ActionBuilders во время выполнения, поэтому они не используются (они просто создаются и удаляются).

В попытке сохранить все проверки внутри сценария, сохраняя определение HTTP-запросов для другой функции, я попробовал следующую альтернативу:

def echo(): HttpRequestBuilder = {
    http("Postman Echo Service Call")
        .get("https://postman-echo.com/get")
        .headers(headers("headers_0"))
        .formParamMap("${schema_attributes}")
}

// Used as
.feed(myFeeder)
.exec { session => session.set("schemaAttributes", session.attributes.filterKeys(key => key.startsWith("some_prefix_")))
}
.exec(
    echo()
        .check(bodyString.saveAs("responseBody"))
)

Таким образом, у меня проблема в том, что если карта пуста (т.е. информация отсутствует в файле) formParamMap не будет работать. Есть ли способ при желании добавить formParamMap, если карта действительно содержит данные?

В общем, существует ли предпочтительный шаблон / метод для передачи параметров функциям, которые возвращают HttpRequestBuilders из сценария? Например, если у меня есть другая функция, которая принимает в качестве параметра Int и создает HTTP-запрос с номером в отправляемом запросе, как я могу это сделать?


person George E. Kallergis    schedule 13.02.2020    source источник
comment
gatling.io/docs/current/general/scenario См. предупреждение о неизменяемых ActionBuilders.   -  person George Leung    schedule 13.02.2020
comment
Привет, Джордж! Спасибо за быстрый ответ. Проверяя отрывок, на который вы ссылались, я понимаю, что http, get и все DSL-функции Gatling являются неизменяемыми конструкторами и не выполняют никаких конкретных действий, если они не используются внутри блока exec. Версия exec { session => ...; session } не из таких. Только функция exec(actionBuilder: ActionBuilder) будет выполнять действия, определенные моими строителями. Я правильно понял? :) В очередной раз благодарим за помощь.   -  person George E. Kallergis    schedule 14.02.2020


Ответы (1)


В ваших примерах echo возвращает HttpRequestBuilder. Как работает gatling, все построители должны создаваться при запуске, а затем использоваться для выполнения запросов, поэтому, когда вы включаете это в функцию сеанса, Builder создается во время выполнения, но тогда фактически никогда не выполняется и неизмененный сеанс возвращается.

Чтобы это сработало, вам нужно переместить echo из функции сеанса. Как вы уже поняли, вам, вероятно, придется изменить сигнатуру метода, чтобы принять какое-то выражение

person James Warr    schedule 13.02.2020
comment
Привет, Джеймс! Спасибо за ответ. Другие функции, которые у меня есть, я использую с exec(actionBuilder: ActionBuilder), и они вроде работают нормально. Думаю, проблема в exec(sessionFunction: Expression[Session]) версии, которую я использую. Если я переместил функцию в блок exec(actionBuilder: ActionBuilder), я мог бы передать переменную сеанса, например "${extraparamsmap}" в echo, но тогда мне не хватает способа получить фактическую карту, переданную в мою функцию. Есть идеи по этому поводу? Я попробую это сделать и вернусь с новым вопросом, чтобы я мог опубликовать код, а также различные попытки. - person George E. Kallergis; 14.02.2020