Create requests dynamically based on session attributes

Hi,

I’ve searched through the archives but hasn’t yet managed to find a way through this, so here goes. Maybe it can help others as well.

What I’m trying to do: I want to generate requests dynamically, based on a JSON file. Reading and parsing the JSON file is no problem; it’s handled by the getRequests method below. My problem starts when trying to construct a number of HttpRequestBuilder instances based on this; this is what the authenticatedRequest method takes care of.

Of course, the example below doesn’t do anything useful since the result of authenticatedRequest is silently discarded.

val scn: ScenarioBuilder = scenario( “Stress testing” )
.exec( requestChain )
.exec( _.set( “requests”, getRequests( SimulationType.IOS ) ) )
.foreach( “${requests}”, “request” ) {
exec( session => {
authenticatedRequest( session.attributes( “request” ).asInstanceOf[Request], TARGET_URL )

session
} )
}

If I change the Scala block to instead return the value of this method call (removing the “session” at the end), I get a compilation error since the block doesn’t match any of the exec() overloads.

Task :compileGatlingScala
/home/per/git/dna-app-simulations/src/gatling/simulations/dna/app/simulations/DnaIosSimulation.scala:42: type mismatch;
found : io.gatling.http.request.builder.HttpRequestBuilder
required: io.gatling.commons.validation.Validation[io.gatling.core.session.Session]
authenticatedRequest( session.attributes( “request” ).asInstanceOf[Request], TARGET_URL )
^

I can workaround this by using “${foo}” syntax in the authenticatedRequest method call instead and try to do things differently, but I am quite reluctant to do so since my Request object (which is not the Gatling Request class in this case) is a complex class with a bunch of different fields etc; I would really be able to pass the whole instance along to the authenticatedRequest method so that it and its logic can be used to construct the requests. Can it be done or do I need to rethink the approach here a bit?

Big thanks in advance. Banging my head towards the wall doesn’t seem to help in this case unfortunately… :slight_smile:

Best regards,
Per

That won’t work as explained in the doc: https://gatling.io/docs/current/general/scenario/?highlight=builders.

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
}

You must find a different way to create your Gatling requests based on your own data classes.

(Sorry, I apparently forgot to reply to the list in my previous email.)

For the record, this is what I ended up with which works fine for me. Since all my request data is predefined and not dependent on previous responses, I build up the scenario dynamically based on the request data (loaded from a JSON file by the getRequests method).

var scn: ScenarioBuilder = scenario( "Stress testing" )
    .exec( requestChain )

for ( request <- getRequests( SimulationType.IOS ) ) {
    scn = scn.exec( authenticatedRequest( request ) )
}

The other option would be to use a feeder but it seems like a less natural approach here since each user in the simulation should perform all these requests, and even though the documentation (https://gatling.io/docs/3.1/session/feeder/#non-shared-data) describes an approach for this, I think my approach is slightly preferable. It lets me (in authenticatedRequest) perform more sophisticated dynamism such as: select the HTTP method, customize headers per request (based on data in my JSON) etc etc. It doesn’t necessarily fit all use cases, but for my particular use case, this seems to do the job well.

Thanks for pointing me in the right direction Stéphane, much appreciated.

Best regards,
Per

Great you found a way.

FWIW, you could be using foldLeft: https://commitlogs.com/2016/09/10/scala-fold-foldleft-and-foldright/

Thanks for the suggestion, I tried it to see how it felt My colleagues are more Java-oriented than Scala-oriented so I think we’ll prefer the other (non-Scala-idomatic) way in this case, but if you want to go all-in with the Scala way of thinking, your suggestion makes perfect sense.

Best regards,
Per