Feeding session data into exec

Hi everyone, been struggling with this for over a week now, reading through every doc I could get my hands on and trying every combination - but I’m very new to Scala/Gatling/functional languages (normally C#, Python, etc), and I just don’t know enough to understand what I am looking for… (inherited this project from someone who knew scala much better)

Renaming / cleaning some stuff out for discussion purposes…

`

def http_req_first(member_index: String) = {
val member = created_members(member_index.toInt)
val first_name = member(“first_name”)

http("/some/endpoint")
.post("/some/endpoint")
.header(“Authorization”, “${header.Authorization}”)
.header(“Content-Type”, “application/json”)
.body(StringBody(
s"""{

“firstName”: “${first_name}”
}""".stripMargin))
.check(status is 200)
.check(bodyString.saveAs(“json_response”))
.check(jsonPath("$.returnId").saveAs(“returnId”))
}

val scn_simple_cycle = scenario(“MyScenario”)
.forever(“userCycleCounter”) {
feed(janus_users.circular)
.exec { session => session.set(“member_index”, next_member(session(“userCycleCounter”).as[Int])) }
.doIf(validation_on) {
exec( http_req_first(s"""${member_index}""") )
}.pause(execution_delay)
}

`
Fails (as we’d expect) with “not found: value member_index” because member_index isn’t defined as a normal variable

... exec( http_req_first(session("member_index")) ) ...
Fails with “not found: value session” because the session isn’t available
(if I don’t pass in a session variable then I get back a ChainBuilder(List(io.gatling.http.action.HttpRequestActionBuilder@2eae81f4)))

`

exec( session => http_req_first(session(“member_index”).as[String]) )

[or]

exec( session => {
http_req_first(session(“member_index”).as[String])
} )


`
Fails with "

type mismatch;

found : io.gatling.http.request.builder.HttpRequestBuilder

required: io.gatling.commons.validation.Validation[io.gatling.core.session.Session]

exec( session => http_req_first(session(“member_index”).as[String]) )

^

" which seems to want us to return session if we pass the session in (for the purpose of retrieving that variable we stored)

So a lot of places online recommend doing it this way

`

exec( session => {

http_req_first(session(“member_index”).as[String])

session

} )

`
which gets past there (returns a ChainBuilder(List(io.gatling.core.action.builder.SessionHookBuilder@7672e998))), and the value from session(“member_index”) gets passed along fine…
and then we fail at the end of the test-run with “Exception in thread “main” java.lang.ArithmeticException: / by zero”, which seems to be because we override session with the session we passed in, and lose the results of whatever happened in the http request? which Gatling seems to need for something? Probably wanted to see Validate[Session]?

I just don’t know what I’m looking at wrong / not quite getting what is happening under the hood. And I’ve tried dozens of other combinations and things, most of which I can’t even remember now

Why does passing in a session to an anonymous function require a session out? If I don’t pass a session in then http_req_first returns a ChainBuilder containing an HttpRequestActionBuilder which seems to be good enough, unless I pass a session in

What’s the correct way of doing this? What am I missing?

I don’t know which doc you’ve been reading but if they suggest creating an http request in a exec(function) they are completely wrong.

Please have a look at the official documentation :
• why how you are trying to do things doesn’t work: https://gatling.io/docs/current/general/scenario/#exec
• how you should be crafting a dynamic StringBody: https://gatling.io/docs/current/http/http_request/#request-body

hmm… I am not sure I quite understand what you mean, the link you provided has an http request in an exec? (I’ve actually been referring to both of those links to get that far)

scenario("My Scenario")
  .exec(http("Get Homepage").get("http://github.com/gatling/gatling"))

and the second link doesn’t actually cover how to get ‘session’ passed in - if there is a more ‘best-practices’ way to do this I would love to see it in that document.

Since then though I’ve kept hacking and hacking … and hacking at it and it does seem I’ve found some workarounds that seems to work -

`

def http_req_first() = {

http("/some/endpoint")
.post("/some/endpoint")
.header(“Authorization”, “${header.Authorization}”)
.header(“Content-Type”, “application/json”)
.body(StringBody(
s"""{

“firstName”: “${session(“firstName”)}”
}""".stripMargin))
.check(status is 200)
.check(bodyString.saveAs(“json_response”))
.check(jsonPath("$.returnId").saveAs(“returnId”))
}

def get_first_name (member: Map[String, String]) = { member(“first_name”) }

val scn_simple_cycle = scenario(“MyScenario”)
.forever(“userCycleCounter”) {
feed(janus_users.circular)
.exec { session => session.set(“member_index”, next_member(session(“userCycleCounter”).as[Int])) }
.exec { session => session.set(“member”, created_members(session(“member_index”).as[Int])) }
.exec { session => session.set(“firstName”, get_first_name(session(“member”).as[Map[String, String]])) }

.doIf(validation_on) {
exec( http_req_first() )
}.pause(execution_delay)
}

`

I’m embarrassed to say that I don’t entirely know why this path seemed to work, except that I’ve learned 999 things that didn’t.

First link:

Warning

Gatling DSL components are immutable ActionBuilder(s) that have to be chained altogether and are only built once on startup. The results is a workflow chain of Action(s). These builders don’t do anything by themselves, they don’t trigger any side effect, they are just definitions. As a result, creating such DSL components at runtime in functions is completely meaningless. If you want conditional paths in your execution flow, use the proper DSL components (doIf, randomSwitch, etc)

exec { session =>

  if (someSessionBasedCondition(session)) {
    // just create a builder that is immediately discarded, hence doesn't do anything
    // you should be using a doIf here
    http("Get Homepage").get("[http://github.com/gatling/gatling](http://github.com/gatling/gatling)")
  }
  session
}

Second link:

.body(StringBody(session => """{ "myContent": """" + someGenerator(session) + """" }""")).asJson

I suspect you’re too used to imperative programming that you’re missing what a function is, basically some piece of code to be passed as a parameter so it can be executed by someone else.
Maybe this beginner explanation in Java would help (a lambda is an anonymous function): https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html