0

I am new to Gatling, I am trying to do the performance testing for couple of rest calls. In my scenario I need to extract a value from json response of the 1st call and add those values to the list after looping for few times. Again after looping for few times and adding the values into the list, I want to reuse each value in my next rest call by iterating over the values in the list. Can anyone please suggest on how to implement this. I tried something as below,

    var datasetIdList = List.empty[String]
    val datasetidsFeeder = datasetIdList.map(datasetId => Map("datasetId" -> datasetId)).iterator
    
    def createData() = {
      repeat(20){
      feed("").exec(http("create dataset").post("/create/data").header("content-type", "application/json")
            .body(StringBody("""{"name":"name"}"""))
            .asJson.check(jsonPath("$.id").saveAs("userId"))))
.exec(session => { var usrid = session("userId").as[String].trim
          datasetIdList:+= usrid session})
    }}
    def upload()= feed(datasetidsFeeder).exec(http("file upload").post("/compute-metaservice/datasets/${datasetId}/uploadFile")
            .formUpload("File","./src/test/resources/data/File.csv")
            .header("content-type","multipart/form-data")
            .check(status is 200))
  
    val scn = scenario("create data and upload").exec(createData()).exec(upload())
    setUp(scn.inject(atOnceUsers(1))).protocols(httpConf)
    }

I am seeing an exception that ListFeeder is empty when trying to run above script. Can someone please help

Updated Code:

class ParallelcallsSimulation extends Simulation{

  var idNumbers = (1 to 50).iterator

  val customFeeder = Iterator.continually(Map(
    "name" -> ("test_gatling_"+ idNumbers.next())
  ))

  val httpConf = http.baseUrl("http://localhost:8080")
    .header("Authorization","Bearer 6a4aee03-9172-4e31-a784-39dea65e9063")

  def createDatasetsAndUpload() = {
    repeat(3) {
      //create dataset
      feed(customFeeder).exec(http("create data").post("/create/data").header("content-type", "application/json")
        .body(StringBody("""{ "name": "${name}","description": "create data and upload file"}"""))
        .asJson.check(jsonPath("$.id").saveAs("userId")))
        .exec(session => {
          val name = session("name").asOption[String]
          println(name.getOrElse("COULD NOT FIND NAME"))
          val userId = session("userId").as[String].trim
          println("%%%%% User ID ====>"+userId)
          val datasetIdList = session("datasetIdList").asOption[List[_]].getOrElse(Nil)
          session.set("datasetIdList", userId :: datasetIdList)
        })
    }
  }
 // File Upload
  def fileUpload() = foreach("${datasetIdList}","datasetId"){
    exec(http("file upload").post("/uploadFile")
      .formUpload("File","./src/test/resources/data/File.csv")
      .header("content-type","multipart/form-data")
      .check(status is 200))
  }

  def getDataSetId() = foreach("${datasetIdList}","datasetId"){
      exec(http("get datasetId")
        .get("/get/data/${datasetId}")
        .header("content-type","application/json")
        .asJson.check(jsonPath("$.dlp.dlp_job_status").optional
        .saveAs("dlpJobStatus")).check(status is 200)
      ).exec(session => {
        val datastId = session("datasetId").asOption[String]
        println("request for datasetId >>>>>>>>"+datastId.getOrElse("datasetId not found"))
        val jobStatus = session("dlpJobStatus").asOption[String]
        println("JOB STATUS:::>>>>>>>>>>"+jobStatus.getOrElse("Dlp Job Status not Found"))
        println("Time: >>>>>>"+System.currentTimeMillis())
        session
      }).pause(10)
    }

  val scn1 = scenario("create multiple datasets and upload").exec(createDatasetsAndUpload()).exec(fileUpload())
  val scn2 = scenario("get datasetId").pause(100).exec(getDataSetId())

  setUp(scn1.inject(atOnceUsers(1)),scn2.inject(atOnceUsers(1))).protocols(httpConf)
}

I see below error when I try to execute above script [ERROR] i.g.c.s.LoopBlock$ - Condition evaluation crashed with message 'No attribute named 'datasetIdList' is defined', exiting loop

har123
  • 49
  • 6

1 Answers1

1

var datasetIdList = List.empty[String] defines a mutable variable pointing to a immutable list.

val datasetidsFeeder = datasetIdList.map(datasetId => Map("datasetId" -> datasetId)).iterator uses the immutable list. Further changes to datasetIdList is irrelevant to datasetidsFeeder.


Mutating a global variable with your virtual user is usually not a good idea. You can save the value into the user's session instead.


In the exec block, you can write:

val userId = session("userId").as[String].trim
val datasetIdList = session("datasetIdList").asOption[List[_]].getOrElse(Nil)
session.set("datasetIdList", userId :: datasetIdList)

Then you can use foreach to iterate them all without using a feeder at all.

foreach("${datasetIdList}", "datasetId") {
  exec(http("file upload")
    ...
}

You should put more work in your question.

  • Your code is not syntax-highlighted, and is formatted poorly.
  • You said "I am seeing an exception that ListFeeder is empty" but the words "ListFeeder" are not seen anywhere.
  • You should post the error message so that it's easier to see what went wrong.

In the documentation linked, there is a Warning. Quoted below:

Session instances are immutable!

Why is that so? Because Sessions are messages that are dealt with in a multi-threaded concurrent way, so immutability is the best way to deal with state without relying on synchronization and blocking.

A very common pitfall is to forget that set and setAll actually return new instances.

This is why the code in the updated question doesn't update the list.

session => {
  ...
  session.set("datasetIdList", userId :: datasetIdList)
  println("%%%% List =====>>>" + datasetIdList.toString())
  session
}

The updated session is simply discarded. And the original session is returned in the anonymous function.

George Leung
  • 1,209
  • 1
  • 7
  • 11
  • I tried what you have suggested, but when I use foreach it is not executing that block because data is not added to the list. I see userids printed in console but is not added to the List as below . Please see the updated code above. test_gatling_ptpt14_2 %%%%% User ID ====>6fd1a074-b0ec-46a3-8821-c0558cd96384 %%%% List =====>>>List() – har123 Mar 24 '21 at 16:24
  • @har123 I quoted the documentation that describes your error. – George Leung Mar 24 '21 at 18:24
  • In the documentation I see the proper usage as session.set("foo", "FOO").set("bar", "BAR"). but how do I implement this in my scenario since I am adding data dynamically for every iteration ? – har123 Mar 24 '21 at 22:16
  • @har123 if you read the code I provided and you copy-and-pasted, you can see that it's already accumulating values for every iteration. If only you returned the updated session (instead of the original copy) in the lambda, you can use the list in the actions that follow. – George Leung Mar 24 '21 at 22:20
  • I don't get it when you say updated session, Are you saying I need to assign it to different list or Session and then return it ? – har123 Mar 24 '21 at 23:01
  • @har123 See [this SO question](https://stackoverflow.com/questions/61246956/gatling-not-storing-value-in-session) for more explanation. – George Leung Mar 24 '21 at 23:28
  • Thanks!! it worked after I removed session in the end and kept session.set as last line. But it is working only within that scenario. Is there a way that I can use it across multilple scenarios? because I am trying to execute 2 scenarios parallely something like val scn1 = scenario("create data and upload").exec(createDatasetsAndUpload()).exec(fileUpload()) val scn2 = scenario("get data").pause(100).exec(getData()) setUp(scn1.inject(atOnceUsers(1)),scn2.inject(atOnceUsers(1))).protocols(httpConf) – har123 Mar 25 '21 at 17:47
  • @har123 If you need to share data across scenarios, you have to use a shared mutable object. Something like [this](https://stackoverflow.com/questions/66777405/how-to-access-java-object-from-gatling-feeder). – George Leung Mar 25 '21 at 19:15
  • I don't see the solution in the link you provided, Do you suggest me to use ConcurrentLinkedDeque ? Please check the updated code above of what I am trying to do – har123 Mar 25 '21 at 20:38
  • @har123 In your initial question can be handled by using session attributes. Now you want to pass information across scenarios. You should ask another question, or google [gatling share data between scenarios](https://stackoverflow.com/questions/28217667/share-data-between-gatling-scenarios). – George Leung Mar 26 '21 at 00:52