Gatling: how to execute the first request only once? - scala

I'm trying to run a scenario which has 2 requests
The first one authenticates the users through an API and gets a token. I want each user to do the first request only once.
The second one executes a GET throught another API.
Here's the code of the scenario:
val slot: ScenarioBuilder = scenario("Get slot")
.doIf(_("connected").asOption[String].isEmpty) {
exec(
http("Get_token")
.post(my_endpoint)
.headers(specific_headers)
.formParam("username", "my_username")
.formParam("password", "my_password")
.check(regex("""id_token":"(.*?)"""").saveAs("my_token"))
)
.exec { session =>
println("Before: " + session("connected").as[String]) //prints nothing as expected
session
}
.exec(_.set("connected", "true"))
.exec { session =>
println("After: " + session("connected").as[String]) //prints "true" as expected
session
}
}
.feed(fsid)
.exec(
http("Get_slot")
.get("/slots")
.headers(other_specific_headers) //including "my_token" extraced above
.queryParam("specific_id", "${specific_id}") //from feeder fsid
.check(regex("""id":"(.*?)"""").findRandom.saveAs("slot_id"))
)
Here's the code of the simulation:
setUp(
slot.inject(
constantUsersPerSec(1).during(15.seconds)
).protocols(httpConfig)
)
When I execute the above simulation, I can see in the logs that each of the 2 requests are both executed 15 times. I'm expecting to have less "Get_token" executed than "Get_slot".
I've read some documentation avout Gatling's forever element, and I think it could do the work.
But I would like to understand why this does not behave as I expect.
If both requests are executed 15 times, does it mean that:
15 users are launched, and thus their session is empty at the beginning, so it's normal they do the get_token ?
My "Doif" is not well written ?
Something else I'm missing ?
Any help would be great, thank you in advance.
EDIT:
Just to give more informations, according to simpleApp's comment, I only see 2 actives users during the test (in average)
This is why I expect to see 4 "Get_token", and not more.

Related

Retry failed request with pause in gatling

I'm trying to implement retry logic so that if my request fails due to some technical problem (timeout, connection reset etc.) or error code 4xx|5xx, script tries to re-submit it couple of times with some pause
My code looks like this
scenario("my_simulation")
.repeat(2) {
tryMax(5, "retryLoopIndex") {
//pause(session => computePause(session("retryLoopIndex").as[Int]))
println(LocalDateTime.now() + """Before sleep""")
pause(5.seconds)
println(LocalDateTime.now() + """After sleep""")
exec(http("get_eps_internal")
.get("/500")
.headers(headers_0)
.requestTimeout(GlobalRequestTimeout)
.check(status.is(200))
)
}
}
I have 2 problems here:
it seems like pause "happens" only once when scenario gets initialized
it pauses for couple milliseconds, not 5 seconds as I would expect
From the logs
2022-02-04T08:23:06.404 Before sleep
2022-02-04T08:23:06.408 After sleep
Ho can I add pause before each retry?
The way you are calling the println() is not correct. In fact, println() called once and before starting the load test. If you want to call during the test you need to do this:
.exec { session =>
println(LocalDateTime.now() + """Before sleep""")
session
}
Another point is that you call actions without a "dot" and it isn't a right way
... {
pause(5.seconds)
exec(http("get_eps_internal")
... }
You need do this:
... {
pause(5.seconds)
.exec(http("get_eps_internal")
... }

Loop over multiple response matches from previous request in Gatling / Scala

I'm still pretty new to Gatling / Scala, so my apologies if I've misunderstood something obvious, but...
I have a scenario with a sequence of requests. One of them is along the lines of:
.exec (
http("Get IDs")
.post(<urlpath>)
.body(<body text>)
.headers(<headerinfo>)
.check(jsonPath("$[*].some.json.path").findAll.transform(_.map(_.replace("unwantedchars,""))).saveAs(myIds)
)
And that works fine, returning a vector of all matching json elements, with the unwanted chars removed. What I'm trying to do next is loop over the first 5 ids, and pass them into the next request. I've tried assorted variants of the following, but no amount of variation / googling has returned the actual solution:
.exec( session => {
val firstFive = session("myIds").as[Vector[String]].toArray.take(5)
for (inx <- 0 until 4){
exec(
http("get the item")
.get("/path/to/item/thing/" + firstFive(inx))
.headers(<etc etc>)
)
session
})
So I understand nested exec's aren't supported - but how can I create a block that combines the for-loop, and the actual HTTP requests?
Am I missing something obvious?
if you're looking to take 5, then you just need to put them in the session and then process them with a .foreach block.
so instead of taking the first five and trying to make exec calls (which, as you observed, won't work), save the list back to the session
.exec( session => {
val firstFive = session("myIds").as[Vector[String]].take(5)
session.set("firstFive", firstFive)
)
then use the DSL to loop through the collection
.foreach("${firstFive}", "id") {
exec(
http("get the item")
.get("/path/to/item/thing/${id}")
.headers(<etc etc>)
)
}
you could even add the selection of the first five to your transform step - so the whole thing could be...
.exec (
http("Get IDs")
.post(<urlpath>)
.body(<body text>)
.headers(<headerinfo>) .check(jsonPath("$[*].some.json.path").findAll.transform(_.map(_.replace("unwantedchars,"")).take(5).saveAs(myIds)
)
.foreach("${myIds}", "id") {
exec(
http("get the item")
.get("/path/to/item/thing/${id}")
.headers(<etc etc>)
)
}

Protractor: After completing its task, it Timeouts

I have been trying to automate a case in which i have to create a group of Urls. So after executing the below script all of the groups that are required are added. After completing all of its task , it is throwing timeout error. But the same objects when used in other specs works perfectly.
describe('Test for ToolbarExpandField',function(){
it('Creating a new url group',function(){
emulator.createNewURLGroup(URLGroupName,URLGroupList);
})
})
createNewURLGroup:function(URLGroupName,URLGroupList){
base.click(base.byElement(base.getLocator(emulatorObjects.dropUpBodyOption,['New URL Group'])));
emulatorObjects.uRLGroupNameField.sendKeys(URLGroupName);
browser.waitForAngular();
base.click(emulatorObjects.confirmButton);
expect(base.byElement(base.byCss("option[value = '"+URLGroupName+"']")).getText()).toEqual(URLGroupName);
for(var i = 1; i<URLGroupList.length ; i++){
tsHelper.checkPresence(emulatorObjects.addNewUrlDiv,true);
base.click(emulatorObjects.addNewUrlDiv);
emulatorObjects.urlNameField.sendKeys(URLGroupList[i].name);
emulatorObjects.urlLinkField.sendKeys(URLGroupList[i].link);
base.click(emulatorObjects.saveUrlDetails);
}
tsHelper.checkPresence(emulatorObjects.addNewUrlDiv,false);
base.click(emulatorObjects.confirmButton);// Errors occur here
}
The purpose of testing is to check and test something. So, each test case should have some expectation and it's result. That's why when you used it in some other test cases, it worked, because those test cases must be already having some expectation.
You can add expectation to Creating a new url group test case after calling createNewURLGroup function or if you don't have anything to check, then you can just add expectation which is always true (Not a good way):
Example:
it('Creating a new url group',function(){
emulator.createNewURLGroup(URLGroupName,URLGroupList);
expect(true).toBeTruthy();
})

waiting for ws future response in play framework

I am trying to build a service that grab some pages from another web service and process the content and return results to users. I am using Play 2.2.3 Scala.
val aas = WS.url("http://localhost/").withRequestTimeout(1000).withQueryString(("mid", mid), ("t", txt)).get
val result = aas.map {
response =>
(response.json \ "status").asOpt[Int].map {
st => status = st
}
(response.json \ "msg").asOpt[String].map {
txt => msg = txt
}
}
val rs1 = Await.result(result, 5 seconds)
if (rs1.isDefined) {
Ok("good")
}
The problem is that the service will wait 5 seconds to return "good" even the WS request takes 100 ms. I also cannot set Await time to 100ms because the other web service I am requesting may take between 100ms to 1 second to respond.
My question is: is there a way to process and serve the results as soon as they are ready instead of wait a fixed amount of time?
#wingedsubmariner already provided the answer. Since there is no code example, I will just post what it should be:
def wb = Action.async{ request =>
val aas = WS.url("http://localhost/").withRequestTimeout(1000).get
aas.map(response =>{
Ok("responded")
})
}
Now you don't need to wait until the WS to respond and then decide what to do. You can just tell play to do something when it responds.

Using Streams in Gatling repeat blocks

I've come across the following code in a Gatling scenario (modified for brevity/privacy):
val scn = scenario("X")
.repeat(numberOfLoops, "loopName") {
exec((session : Session) => {
val loopCounter = session.getTypedAttribute[Int]("loopName")
session.setAttribute("xmlInput", createXml(loopCounter))
})
.exec(
http("X")
.post("/rest/url")
.headers(headers)
.body("${xmlInput}"))
)
}
It's naming the loop in the repeat block, getting that out of the session and using it to create a unique input XML. It then sticks that XML back into the session and extracts it again when posting it.
I would like to do away with the need to name the loop iterator and accessing the session.
Ideally I'd like to use a Stream to generate the XML.
But Gatling controls the looping and I can't recurse. Do I need to compromise, or can I use Gatling in a functional way (without vars or accessing the session)?
As I see it, neither numberOfLoops nor createXml seem to depend on anything user related that would have been stored in the session, so the loop could be resolved at build time, not at runtime.
import com.excilys.ebi.gatling.core.structure.ChainBuilder
def addXmlPost(chain: ChainBuilder, i: Int) =
chain.exec(
http("X")
.post("/rest/url")
.headers(headers)
.body(createXml(i))
)
def addXmlPostLoop(chain: ChainBuilder): ChainBuilder =
(0 until numberOfLoops).foldLeft(chain)(addXmlPost)
Cheers,
Stéphane
PS: The preferred way to ask something about Gatling is our Google Group: https://groups.google.com/forum/#!forum/gatling