Foreach loop after a foreach loop does not execute

Hello!

In my scenario, I have two requests. The first request returns all support languages in the body like so

["de","en","fr","it","es","nl","pl","pt","cs","sk","hu","hr"]

And I save these languages like so:

.check(
            jsonPath("$[0:]")
              .ofType[String]
              .findAll
              .saveAs("languages")
          )

The second request returns JSON data, which contains, among other things

{
...
"pictureId":8,
"language":"fr",
...
},
{
...
"pictureId":4,
"language":"en",
...
},

I save this response like so:

.check(
            jsonPath("$[0:]")
              .ofType[String]
              .findAll
              .saveAs("courses")
          )

I hope you get the idea.

Now, what I need respectively what I want to do, are the following steps:
I need to loop over the second JSON response, per each language, and fire up a request with the property pictureId as a parameter.

My scenario looks like this:

...
val scn: ScenarioBuilder =
    scenario("some random scenario name")
      .exec(courses.httpRequest)
      .exec(languages.httpRequest)
      .foreach(
        session => session("languages").validate[Seq[String]],
        "elem",
        "counter"
      ) {
        exec(
          exec { session =>
            {
              println("+++ elem: " + session("elem").as[String])
              val tempLang: String = session("elem").as[String]
              val vectorWithTempLangFilter: Vector[String] =
                session("courses")
                  .as[Vector[String]]
                  .filter(
                    _.contains("\"language\":\"" + tempLang + "\"") == true
                  )
              val sessionWithTempLangFilter =
                session.set("sessionWithTempLangFilter", vectorWithTempLangFilter)
           
              sessionWithTempLangFilter 
            }
          }
            exec { session =>
              {
                println("before foreach")
                foreach("${sessionWithTempLangFilter}", "vectorElem") {
                  println("in foreach before rounded exec")
                  exec(
                    exec { session =>
                      {
                        println("in foreach in curly exec")
                        println(
                          ">>> vectorElem: " + session("vectorElem").as[String]
                        )
                        session
                      }
                    }
                  )
                }
                println("in foreach after rounded exec")

                session
              }
            }
        )
      }

What I get in the console output is similar like this:

...
+++ elem: en
before foreach
in foreach before rounded exec
in foreach after rounded exec
...

I’m really confused:

  • Why is the second foreach loop executed only once, even I’m a 100% sure, that there are 6 and more elements in the JSON data
  • Why are those lines in the exec(...) not executed? There is an element “vectorElem” in the “session”, isn’t it?

I’m working on this for a week now. Gatling+Scala is a powerful team, but everything deviating from a “straight through” scenario is really frustrating to work with.

Thank you for all your support and help!

Hello!

I think I got the answer by myself. Instead of

...
sessionWithTempLangFilter 
            }
          }
            exec { session =>
              {
                println("before foreach")
...

it needs this

...
sessionWithTempLangFilter
            }
          }.foreach(
            session => session("sessionWithTempLangFilter").as[Vector[Any]],
            "vectorElem"
          ) 
...

That was a though riddle… anyhow, thanks for the support!

Lots of weird things:

  • $[0:] => $[*]
  • no need for ofType[String], this is the default => jsonPath("$[*]").findAll.saveAs("languages")
  • for the courses, why are you saving each element as Strings and not as Map as you’ll want to do a look up? => jsonPath("$[*]").ofType[Map[String, Any]].findAll.saveAs("courses")
  • Why do you have an exec in an exec? One is enough.
  • You’re missing a dot to attach/chain your 2 execs. As a result, only the last one is enabled (passed to the parent exec you should remove)
1 Like

OK… thank you for your answers @slandelle

Can I filter on a Map?

I try to do something like this now

val mapWithTempLangFilter =
                session("courses")
                  .as[Map[String, Any]]
                  .filter( x => x._1 == "language" && x._2 == tempLang)

But I get a casting error like this:

Can't cast attribute ... of type class scala.collection.immutable.Vector2 into interface scala.collection.immutable.Map

But I save the courses like you suggested

.check(
            jsonPath("$[*]")
              .ofType[Map[String, Any]]
              .findAll
              .saveAs("courses")
          )

You can’t call filter on a check. If you want to filter the Map content so you don’t capture the whole JSON objects, you can either use transform, or use a JMESPath check instead (JMESPath has filtering/transforming features). Or you can grab what you want in your exec(function).

OK… I give up on this!

We won’t do load tests with Gatling anymore. It is not the right tool for this. When it is not possible to adapt the scenario accordingly to previous responses, then what is Gatling for? Wondering if no one ever stumbled over this before.

@slandelle You sound very upset with me, sorry for the inconvenience!

I’m not upset, sorry if I sounded this way.

It looks like the main thing you’re struggling with is Scala. Is there any reason why you picked Scala for your Gatling tests and not Java? We really recommend Java if you’re not a Scala or a Kotlin developper.

session("courses")
                  .as[Map[String, Any]]

This doesn’t work because courses is not a Map, it’s a Seq[Map], hence the error message. Remember that you’re using findAll.

Hi @lukas.troellinger,
I have created in JAVA Simulation for your case - I’m not sure if I get it in 100% but below you can find my proposition:
Case0017ForeachAfterForeachSimulation

@slandelle @sbrevet any suggestions to do it better are welcome :slight_smile:

2 Likes

Hello guys!

Thank you very much for your support! In the end, @slandelle was right, my main problem was Scala. But with your help, I could manage to get everything done. Maybe for the sake of completeness, I post my final code here:

...
val languageElement: String = "languageElement"
...
.foreach(
        session => session("languages").validate[Seq[String]],
        languageElement
      ) {
        exec { session =>
          filterCoursesForLanguage(
            session,
            session(languageElement).as[String],
            "sessionWithTempLangFilter"
          )
        }
          .foreach("#{sessionWithTempLangFilter}", "vectorElem") {
            exec { session =>
              {
                var picId: String = ""
                val elem: Map[String, Any] =
                  session("vectorElem").as[Map[String, Any]]

                if (elem("pictureId") != null) {
                  picId = elem("pictureId").toString
                } else {
                  picId = ""
                }
                session.set("pictureId", picId)
              }
            }.doIf(session => session("pictureId").as[String].nonEmpty) {
              group("PictureMedia") {
                exec(AcademyPicturemedia.httpRequest)
              }
            }
          }
      }
...

With this, it works well and I also think that readability is maintained due to using Scala.

Thanks @slandelle and @GeMi for your insights and your help!