I have multiple Gatling simulations that I am trying to structure in a way to reuse low-level calls (i.e. HTTP calls) while building different scenarios. For that reason I have the following structure (echo function is used for the sake of explaining the current state):
// 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 }
...
}
In the test simulation provided above we feed data from a file and select a subset of attributes which we store as a Map into the Session. If such attributes are found in the file we add them as form parameters when submiting the echo request. Otherwise we don't. In both cases we store the response body and print it our as well. This works fine.
When I try to pass the Map as a parameter to echo() is when the issues begin. Suppose I have the following implementation of 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
}
and that the simulation does the following:
.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 }
The requests don't fire and I get the following error about the bodyString missing
[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
An alternative I tried was to make echo accept an Expression[Map[String, Any]]
and use a normal exec block (i.e. not the one with the injected session). In that case I couldn't get the actual map out of the session!
It seems that the bodyString is only available when using the exec() and not the exec {session => ...; session} form. My Scala knowledge is very little which adds to my confusion.
I feel like I am missing something very fundamental about this. Any help will be greately appreciated.
UPDATE #1
Based on the comments received so far, as I understand it using the exec(sessionFunction: Expression[Session])
function creates the ActionBuilders during runtime so they are not used (they are just created and discarded).
In an attempt to keep all checks inside the scenario while keeping the definition of the HTTP requests on a different function I tried the following alternative:
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"))
)
This way I have the problem that if the map is empty (i.e. the information are not present in the file) formParamMap
won't work. Is there a way to optionally add formParamMap if the Map actually contains data?
In general, is there a preferred pattern/method to pass parameters to functions that return HttpRequestBuilder
s from within a scenario? For example, if I have another function that takes an Int as a parameter and creates an HTTP request with the number in the request to be sent, how would I go about doing this?
ActionBuilder
s. - George Leungexec { session => ...; session }
version is not one of those. Only theexec(actionBuilder: ActionBuilder)
function will actually execute actions defined by my builders. Did I get this right? :) Thanks again for your help. - George E. Kallergis