User actions abstraction!

Hello!

I have a few applications that share the same backend and am trying to create a common set of actions to reduce code duplication.

I have the following object to hold the HTTP builders containing nothing but the HTTP request to be sent.

private object HttpRequest {
val default = Map (
“Accept” → “text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3”,
“Accept-Encoding” → “gzip, deflate, br”,
“Accept-Language” → “en-US,en;q=0.9”,
“Upgrade-Insecure-Requests” → “1”,
“User-Agent” → “Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36”
)

def echo(): HttpRequestBuilder = {
http(“Postman Echo Service Call”)
.get(“https://postman-echo.com/get”)
.headers(default)
}

def echo(parameters: Expression[Map[String, String]]): HttpRequestBuilder = {
http(“Postman Echo Service Call”)
.get(“https://postman-echo.com/get”)
.headers(default)
.queryParamMap(parameters)
}

}

In the same package as the above there is a trait that encapsulates those HttpRequestBuilders and adds checks like so:

trait UserAction {
def echo(minimumDuration: Duration, maximumDuration: Duration, checks: Seq[HttpCheck]): ChainBuilder = {
if(checks.isEmpty) {
exec(
HttpRequest.echo()
).exitHereIfFailed.pause(minimumDuration, maximumDuration)
} else {
exec(
HttpRequest.echo().check(checks: _*)
).exitHereIfFailed.pause(minimumDuration, maximumDuration)
}
}

def echo(parameters: Expression[Map[String, String]], minimumDuration: Duration, maximumDuration: Duration, checks: HttpCheck*): ChainBuilder = {
if(checks.isEmpty) {
exec(
HttpRequest.echo(parameters)
).exitHereIfFailed.pause(minimumDuration, maximumDuration)
} else {
exec(
HttpRequest.echo(parameters).check(checks: _*)
).exitHereIfFailed.pause(minimumDuration, maximumDuration)
}
}

}

Now, for each application, I extend the trait to add default checks like so

object UserAction extends UserAction {
def echo(minimumDuration: Duration, maximumDuration: Duration): ChainBuilder = {
val defaultChecks: Seq[HttpCheck] = Seq(
status.is(404),
css(“meta[name=“test”]”, “content”).is(“lalala”)
)
super.echo(minimumDuration, maximumDuration, defaultChecks)
}
}

This way in my scenario I do

private val scenario = scenario(“Test”)
.feed(users)
.exec(
echo(10, 10)
)

To get the default checks for this action (i.e. echo). This works OK so far.

If I add a check in my scenario like so:

private val scenario = scenario(“Test”)
.feed(users)
.exec(
echo(10, 10, Seq(status.is(123)))
)

I get the following error:

overloaded method value echo with alternatives:
(parameters: io.gatling.core.session.Expression[Map[String,String]],minimumDuration: scala.concurrent.duration.Duration,maximumDuration: scala.concurrent.duration.Duration,checks: io.gatling.http.check.HttpCheck*)io.gatling.core.structure.ChainBuilder
(minimumDuration: scala.concurrent.duration.Duration,maximumDuration: scala.concurrent.duration.Duration,checks: Seq[io.gatling.http.check.HttpCheck])io.gatling.core.structure.ChainBuilder
cannot be applied to (Int, Int, Seq[io.gatling.core.check.CheckBuilder[io.gatling.http.check.status.HttpStatusCheckType,io.gatling.http.response.Response,Int] with io.gatling.core.check.SaveAs[io.gatling.http.check.status.HttpStatusCheckType,io.gatling.http.response.Response,Int]])
echo(10, 10, Seq(status.is(123)))

My questions are:

  1. Why the signature that contains the Expression[] is referred to here? Shouldn’t the implementation in the trait be used in this case?
  2. Is there a way to not do the if(checks.isEmpty) and instead overload the mehotd?

My Scala knowledge is very limited so I wouldn’t be surprised if this is just a Scala issue and not a Gatling one!

Thanks for the help.

Kind regards,
George

I think that is indeed a scala issue.

I guess that the scala compilator cannot know which method you are using.

In the latest code, try to import duration wildcard and explicit the unit.

import scala.concurrent.duration._

private val scenario = scenario("Test")
  .feed(users)
  .exec(
    echo(10.seconds, 10.seconds, Seq([status.is](http://status.is)(123)))
  )

I hope that it can help you.

Hello Sébastien,

Thank you for the quick response. :slight_smile:

Explicitly setting these unfortunately didn’t help.

The weird thing is that if I add a test: String parameter to the def echo(parameters: Expression[Map[String, String]], minimumDuration: Duration, maximumDuration: Duration, checks: HttpCheck*) it works OK! :smiley:

I’ll try googling some more and report back here if I find anything. :slight_smile:

Thanks again,
George

Ok, let’s try with explicit naming:

import scala.concurrent.duration._

private val scenario = scenario("Test")
  .feed(users)
  .exec(
    echo(minimumDuration = 10.seconds, maximumDuration = 10.seconds, checks = Seq([status.is](http://status.is)(123)))
  )

Otherwise, please provide a project to test (a github gist?)

Hello Sébastien,

Explicitly defining the names works perfectly fine! I still can’t understand why, but I will attribute that to my lack of Scala knowledge! :smiley:

Thanks again!

Regards.
George