How to set different protocol for each user (threads running) in gatling - scala

I am trying to create a performance test suite where i have array of ID's that will be picked in random. For each ID , there is a designated auth_token assigned .
In the protocol , if i pass the method where the random ID's would take , it always sets it to that particular ID for the entire operation .
I am expecting something like , i am defining 10 virtual users and for each user the protocol should change the ID and continue the scenario execution .
Currently Gatling is setting the protocol at the first and uses the same protocol for all the 10 users.
id = random.generate //generate random id
authHeader = Method(id);
def method (id:String) : String{
if(id=="id1")
return token1
else if(id=="id2")
return token2
""
}
val httpProtocol = http.baseUrl(baseURI)
.acceptHeader(header)
.authorizationHeader(authHeader)
.contentTypeHeader(contentType)
.userAgentHeader(agentHeader)
val scn1: ScenarioBuilder = scenario("name")
.exec(http("scenario1")
.post(device_context)
.body(payload)
.check(status.is(202)))
setUp(scn1.inject(atOnceUsers(2)).protocols(httpProtocol))```
In the above code i need the suite to run for 2 different id.

To make this work, you're going to have to use a session variable to hold your authHeader. The DSL methods define builders that are only executed once - which explains what you're seeing.
The best way to do this would be to construct a feeder to hold your ids and auth tokens (I'm assuming you're not using the id anywhere else)
So define a feeder that maps a key (just a string) to a map that contains the id and auth token
//a list of strings to hold the auth tokens
private val ids = List(Map("id" -> "id1", "auth" -> "token1"), Map("id" -> "id2", "auth" -> "token2"), ...)
//the feeder to put a random token into the session
private val idFeeder = Iterator.continually(Map("id" -> Random.shuffle(ids).head))
now when you call .feed on idFeeder you get a random Map that has keys for "id" and "auth" in the session variable "id"
so update your scenario to use the feeder and set an Authorization header (and remove .authorizationHeader from your protocol definition)
val scn1: ScenarioBuilder = scenario("name")
.feed(idFeeder)
.exec(http("scenario1")
.header("Authorization", "${id.auth})"
.post(device_context)
.body(payload)
.check(status.is(202)))
in your body you can access the user id string with "${id.id}"
alternatively you could update your protocol definition to have the ${id} reference, but I find it nicer to have the feeder value used in the same block as the call to .feed

Related

Mocking input request objects in scala

Is it possible to mock a request object into a mock server? I.e. I want to make a call multiple times but with just one change, the user id in this case: val request = MyServiceRequest(userId = "123", data = "static data"). I.e.
myService.doSomething(MyServiceRequest("1", any()).returns(MyServiceObject(requestId = "1"))
myService.doSomething(MyServiceRequest("2", any()).returns(MyServiceObject(requestId = "2"))
myService.doSomething(MyServiceRequest("3", any()).returns(MyServiceObject(requestId = "2"))
Right now I have to write out the full request each time, I just want to define one parameter.

How to generate multivalue map with random values and inject into request body using a feeder in Gatling

I'm writing a load test for an api and want to create a feeder which generates random values to inject into the body of the POST request. I initially tried copying the random email example from the documentation and adding additional fields to the generated map, but when that didn't work I went down to a single field, basically copying the documentation; however, even this doesn't work for some reason. There are a bunch of solutions on here that use this syntax as well, but something about the way I'm doing is causing the fields I try to inject into the body to be null when the request is made.
Current Code:
val userFeeder: Iterator[Map[String, Unit]] =
Iterator.continually(Map("userName" -> ("user_" + Random.alphanumeric.take(15).mkString)))
var scn: ScenarioBuilder = scenario("GENERATE USER")
.feed(userFeeder)
.exec(
http("CREATE USER")
.post(userBaseUrl)
.headers(userHeaders)
.body(StringBody("userName: ${userName}")))
setUp(
scn.inject(atOnceUsers(1))
)
Ideally I'd like to be able to expand the feeder to include multiple values, i.e.
val userFeeder: Iterator[Map[String, Unit]] =
Iterator.continually(Map("userName" -> ("user_" + Random.alphanumeric.take(15).mkString),
"userEmail" -> (Random.alphanumeric.take(15).mkString) + "#random.edu"),
"address" -> Random.alphanumeric.take(15).mkString)))
and so on, but I'm a little stumped as to why my current code doesn't even work, as it seems to follow the documentation example pretty faithfully. The values are always null in my requests despite trying a few different strategies.
Log output
body:StringChunksRequestBody{contentType='application/json', charset=UTF-8, content=userName: ()}
Figured it out. Turns out that even though Feeder is a wrapper for Iterator, the proper way to do what I want is to declare it like this:
val userFeeder: Feeder[Any] =
Iterator.continually(Map("userName" -> ("user_" + Random.alphanumeric.take(15).mkString),
"userEmail" -> (Random.alphanumeric.take(15).mkString) + "#random.edu"),
"address" -> Random.alphanumeric.take(15).mkString)))

How to access a value stored in a session from within a feeder in Gatling

In my Gatling scenario, a value is stored in the session for a user.
Later in the same scenario, feed is called and passed a custom feeder. The custom feeder needs to generate its next value using the stored value in the session.
val MyScenario = scenario("ScenerioName")
.repeat(10, "repetition") {
exitBlockOnFail {
group("WorkflowGroupName") {
exec(session => {
// SETTING A VALUE INTO THE USER'S SESSION
session.set("sessionVariable", 99) // value that is stored changes for every run of the workflow (99 just for example purposes)
})
// CUSTOM FEEDER THAT GENERATES ITS NEXT VALUE USING THE SESSION VARIABLE 'sessionVariable' STORED ABOVE
.feed(myFeeder)
.group("RequestGroup1") {
exec(httpPost1)
}
}
}
}
val myFeeder = Iterator.continually(Map("jsonFileValue" -> {
// WANT TO RETRIEVE VALUE OF 'sessionVariable' STORED IN THE SESSION
val returnValue = /* logic that generates its value based on value of 'sessionVariable' retrieved */
returnValue
}
))
val httpPost1 = http("Request1")
.post("http://IPAddress/service.svc")
.headers(httpHeaders)
.body(ELFileBody("MyJsonFile.json"))
.check(status.is(200))
val httpHeaders = Map(
"Content-Type" -> "application/json; charset=UTF-8",
"X-Pod" -> ""
)
How can I pass this stored session value to the feeder or have the feeder retrieve this value from the session?
As the documentation states:
Sometimes, you could want to filter the injected data depending on
some information from the Session.
Feeder can’t achieve this as it’s just an Iterator, so it’s unaware of
the context.
If your values are independent of the tests you run, maybe a good way can be to generate a csv before you run your tests and then feed this csv to your tests.

Avoid query parameters duplication when generating a MAC for requests in gatling

I am using gatling (2.1.7) in order to stress test an API.
For of all I have to request a transaction id and shared secret in order to authenticate all subsequent calls.
scenario("API").exec(http("authorize")
.post("/api/v1/xxx/authorize")
.formParam("client_key", "a_very_strong_key")
.check(jsonPath("$.response.txId").saveAs("id"))
.check(jsonPath("$.response.txSecret").saveAs("secret")))
All other calls must contain a query parameter signature which is a mac of the other request parameters.
I wrote this piece of code to do that
scenario("API").exec(http("call")
.get("/api/v1/call")
.queryParam("id", "${id}")
.queryParam("param1", "aaaaaa")
.queryParam("param2", "bbbbbb")
.queryParam("signature", session => sign(session, Map(
"id" -> session("id").as[String],
"param1" -> "aaaaaa",
"param2" -> "bbbbbb"))))
/* ... */
def sign(session: Session, params: Map[String, String]) : String = {
val str = canonicalize(params)
format_mac(session("secret").as[String], str)
}
However I have to duplicate all query parameter names and values in the sign method call and it is clearly a bad practice. It is possible to avoid that ?
You want to sign a request, so use a SignatureCalculator.

Gatling 2 dynamic queryParam on each request

I am trying to run a load test using Gatling 2. I need to generate one of the query parameters dynamically on each request.
My scenario is defined like this:
val scn = scenario("Load Test Scenario")
.exec(
http("Test API")
.post(url)
.body(StringBody("Some XML"))
.queryParam("x", DigestUtils.md5Hex(generateX().getBytes("UTF-8")))
)
def generateX() : String = {
// generate random string and return
}
This only calls generateX once and uses the result in each request. Is there anyway to have the generateX call on every request?
You have to pass a function, not a value. See Gatling documentation about Expression.
Here, you can just discard the session input parameter as you don't use it, so you can simply write:
.queryParam("x", _ => DigestUtils.md5Hex(generateX().getBytes("UTF-8")))