I can't get a value from one of two feeders - scala

I have two feeders in my test scenario
When i use one of them in first request it works alright, however when I use it in the next request block it doesn't work
I have tried changing the feed(feederName) position but still having the same problem
Here is a snippet of my test scenario with some comment to explain what's not working
//the Two feeders
val kmPerYearFeeder = Iterator.continually(
Map("kmPerYear" -> Random.shuffle(List("10000", "15000", "20000", "25000", "30000", "35000", "40000", "45000", "50000")).head)
)
val customerTypes = Iterator.continually(
Map("customerType" -> Random.shuffle(List("P","B")).head)
)
//here the customerTypes feeder is working
val homepage = feed(customerTypes)
.exec(http("homepage")
.get("/?customer_type=${customerType}"))
//this block is not really important but working alright
val pdp = exec(http("homepage")
....
// the feeder here doesn't work
val calculate_rate = feed(kmPerYearFeeder)
.exec(http("calculate_random_rate")
.get(session => session("random_pdp_link").as[String] + "?inquiry_type=&km_per_year=${kmPerYear}")
.check(status.is(200)))
val pdp_scenario = scenario("PDP").exec(homepage).exec(pdp).exec(calculate_rate)
setUp(
pdp_scenario.inject(
rampUsers(10) during (5 seconds),
).protocols(httpProtocol),
)
these are the get requests that are executed (got them from the logger)
GET ********?inquiry_type=&km_per_year=$%7BkmPerYear%7D
GET ********?inquiry_type=&km_per_year=$%7BkmPerYear%7D
GET ********?inquiry_type=&km_per_year=$%7BkmPerYear%7D

Your issue is that in where you attempt to use the Gatling EL to reference the kmPerYear session var in
.get(session => session("random_pdp_link").as[String] + "?inquiry_type=&km_per_year=${kmPerYear}")
this version of .get takes a session function (which you are using to get "random_pdp_link"), but the Gatling EL doesn't work in session functions.
You either need to get it manually using
session("kmPerYear").as[String]
or to reference "random_pdp_link" via the EL and not use the session function get. eg:
.get("${random_pdp_link}?inquiry_type=&km_per_year=${kmPerYear}")

Related

Value for a session is replicated between sessions

I have a load test where a session value is set depending on the URL that I request and can be one of two options which I choose randomly.
When I execute the test for one user, the value in the session is set successfully with a random value.
When I add more users the value would be set randomly but also the same for all the sessions of all users (so all users have the same session value)
My load test looks like this
class test extends Simulation {
val customer_types = Array("P", "B")
val httpProtocol = http
.baseUrl("https://www.test.com")
.inferHtmlResources()
.acceptHeader("*/*")
.acceptEncodingHeader("gzip, deflate")
.acceptLanguageHeader("en-US,en;q=0.5")
.userAgentHeader("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0")
val homepage = exec(http("homepage")
.get("/?customer_type=" + Random.shuffle(customer_types.toList).head.toString))
.exec { session =>
println(session)
session
}
val home_page_scenario = scenario("PDP").exec(homepage)
setUp(
home_page_scenario.inject(
rampUsers(10) during (5 seconds),
).protocols(httpProtocol),
)
}
After running the test with one user, I get the following session values
with customer_type equals either P or B
Session(PDP,10,1565009885331,Map(gatling.http.cache.baseUrl -> https://test.test.com, gatling.http.cache.dns -> io.gatling.http.cache.DnsCacheSupport$$anon$1#13cea563, gatling.http.cache.contentCache -> io.gatling.core.util.cache.Cache#1acc69e3, gatling.http.ssl.sslContexts -> SslContexts(io.netty.handler.ssl.OpenSslClientContext#534581dd,None), gatling.http.referer -> https://test.test.com/?customer_type=B, gatling.http.cookies -> CookieJar(Map(CookieKey(customer_type,www.test.com,/) -> StoredCookie(customer_type=B, path=/, secure,true,false,1565009892233), CookieKey(test_session,test.com328110,/) -> StoredCookie(test_session=OS96ekJ4Nk0zRkJBak5obDdES0RZaW1Qb1hHS1U1VG5YcndGbmxKT1hrV3p4WVpCZElSUXJISVlZRlZtNjRmazd4QVlYTHhlWHFyUjJIU2VLZUh1Q083NjFmVlFVdzNLMmVwckh5Z0JuOWJLaW1ab2FIbU13Qnl0UVdZblFSOHlrVXJWYUZTQ3dYL1ZOV1dZM2Z0MWxGK1piN1lpbGdTRUdZeXBGQXllaHBPcW83eW0zTStuc1huelJOZzRPNkFBN2RKN281Y2FvSUU0V01BTVk5RmtWQT09LS1nbEMxV1FId3MvT0ZaVFBFV2YwVGZnPT0%3D--5a89e73be05416d96f3acf2e3f927495caf3d491, domain=.test,cin, path=/, secure, HTTPOnly,false,false,1565009892233), CookieKey(user_group,www.test.com,/) -> StoredCookie(user_group=n, path=/,true,false,1565009892233)))),0,OK,List(),io.gatling.core.protocol.ProtocolComponentsRegistry$$Lambda$531/0x0000000840511040#6360231f)
After running it with the 10 users however, i get all the sessions with either P or B for customer type but it's the same for all users.
The gatling DSL specifices builders that are built ONCE at the start of a run - these are then used to create the users that execute scenarios.
Because of this, your Random.shuffle(customer_types.toList).head.toString is only executed once and all the users pick up this value.
To have a random customer_type selected by each user, you can create a custom feeder.
private val customerTypes = Iterator.continually(
Map("customerType" -> Random.shuffle(List("P","B")).head)
)
...
val homepage = exec(http("homepage")
.get("/?customer_type=${customerType}")
.exec { session =>
println(session)
session
}
)
val home_page_scenario = scenario("PDP").feed(customerTypes).exec(homepage)
so now each user will get the next value from the customerTypes feeder (which has more or less the same randomising function as your original example)
As James Warr's answer have mentioned, the execution of Random.shuffle in your code happens run only once as the test is being set up. In fact this is a common misunderstanding for new users of Gatling.
For dynamic behaviour, the Expression Language (EL) of Gatling should be enough for most cases.
If you just want a random element, you can simply use the .random() EL function. Shuffling the whole list is too pointlessly costly.
"${foo.random()}" // returns a random element of `foo` if `foo` points to an indexed collection
But to use the EL, the variable has to be put into the session attribute. This can be accomplished by using an exec block.
.exec { session => session.set("customerTypes", customerTypes)}
.exec(http("homepage")
.get("/")
.queryParam("customer_type", "${customerTypes.random()}"))

Gatling Need to run next scenario if previous scenario is passed using doIf

i am new to Scala and gatling. i need to run scenaio if previous scenario is passed using doIf.
My code is:
HttpRequest
object CompanyProfileRequest {
val check_company_profile: HttpRequestBuilder = http("Create Company
Profile")
.get(onboarding_url_perf + "/profile")
.headers(basic_headers)
.headers(auth_headers)
.check(status.is(404).saveAs("NOT_FOUND"))
val create_company_profile: HttpRequestBuilder = http("Create Company
Profile")
.post(onboarding_url_perf + "/profile")
.headers(basic_headers)
.headers(auth_headers)
.body(RawFileBody("data/company/company_profile_corporation.json")).asJson
.check(status.is(200))
.check(jsonPath("$.id").saveAs("id"))
}
Scenario class is:-
object ProfileScenarios {
val createProfileScenarios: ScenarioBuilder = scenario("Create profile
Scenario")
.exec(TokenScenario.getCompanyUsersGwtToken)
.exec(CompanyProfileRequest.check_company_profile)
.doIf(session => session.attributes.contains("NOT_FOUND")) {
exec(CompanyProfileRequest.create_company_profile).exitHereIfFailed
}
}
And Simulation is :-
private val createProfile = ProfileScenarios
.createProfileScenarios
.inject(constantUsersPerSec(1) during (Integer.getInteger("ramp", 1)
second))
setUp(createProfile.protocols(httpConf))
Whenever i am running this simulation, I am not able to check this condition:-
.doIf(session => session.attributes.contains("NOT_FOUND"))
Any help is much appreciated.
Regards,
Vikram
I was able to get your example to work, but here's a better way...
the main issue with using
.check(status.is(404).saveAs("NOT_FOUND"))
and
.doIf(session => session.attributes.contains("NOT_FOUND"))
to implement conditional switching is that you've now got a check that will cause check_company_profile to fail when it really shouldn't (when you get a 200, for example).
A nicer way is to use a check transform to insert a boolean value into the "NOT_FOUND" variable. This way, your check_company_profile action can still pass when the office exists, and the doIf construct can just use the EL syntax and be much clearer as to why it's executing.
val check_company_profile: HttpRequestBuilder = http("Create Company Profile")
.get(onboarding_url_perf + "/profile")
.headers(basic_headers)
.headers(auth_headers)
.check(
status.in(200, 404), //both statuses are valid for this request
status.transform( status => 404.equals(status) ).saveAs("OFFICE_NOT_FOUND") //if the office does not exist, set a boolean flag in the session
)
now that you've got a boolean session variable ("OFFICE_NOT_FOUND") you can just use that in your doIf...
.doIf("${OFFICE_NOT_FOUND}") {
exec(CompanyProfileRequest.create_company_profile).exitHereIfFailed
}

Gatling pass data from feeder to exec

Could you help me how to get data from feeder in exec block? When I use exec with session input parameter is not a problem, but when I want to use with ActionBuilder I don't know how to do it. Documentation says to use Gatling EL, but seems it can be use only with http protocol. I want to use gRPC protocol, so I can't use methods http().
Do you have any idea how to do it?
My code:
def runAction : ActionBuilder = {
//TODO how to get parameter1 and parameter2 from session or feeder here.
}
def getRandomData() = Map("parameter1" -> UUID.randomUUID.toString, "parameter2" -> (Random.nextInt(100000) + 700000))
val feeder = Iterator.continually(getRandomData())
scenario("TestGRPC server")
.feed(feeder)
.exec(session => {
val parameter1= session("parameter1").as[String] //here is works
val parameter2= session("parameter2").as[Int] //here is works
println("parameter1: " + parameter1+ "parameter2: " + parameter2) //print diffrent for each execution
session
})
.exec(runAction)
setUp(
grpcScenario.inject(
constantUsersPerSec(2) during (durationInSeconds seconds))
).protocols(grpcConfig)
according to the gatling-grpc documentation, the payload can indeed take an expression.
which means you should be able to have code like this in 'runAction'
grpc("request name")
.rpc(rpc.method)
.payload("parameter1: ${parameter1}")

Need list outside map in play ws api call

In a play-scala application, Im making a WS call:
def getaddresses : ListBuffer[String] = {
var lb = new ListBuffer[String]()
var url = "xyz#xyx.com/someapi"
WS.url(url).get.map {
response =>
var emailsSeq = (response.json \\ "email")
emailsSeq.foreach(lb+=_.toString())
lb.foreach(println) //This is filled with values
}
lb.foreach(println) //This is empty
lb
}
Inside map a sequence is returned whose entries I put in a ListBuffer lb to be returned by this method.
The problem is that the the ListBuffer shows values inside map{} but does not show outside it. As a result empty ListBuffer is passed from the method.
I thought there might be a delay in ws response so tried the following which had no advantage:
WS.url(url).withRequestTimeout(10.seconds).get.map {
Please help me in getting filled list buffer to be returned
I think that ws.url(url).get method is async, so when you call lb.foreach(println), there is nothing to print. Try to add Thread.sleep just after map block of code. If it is so, you should make getaddresses method async, or use future/promise.
PS: you should use val instead of var, just for cleaner code.
EDIT: Sample to try:
def getaddresses : ListBuffer[String] = {
val url = "xyz#xyx.com/someapi"
val promiseOfAddresses = Promise[ListBuffer[String]]()
WS.url(url).get.map {
response =>
var emailsSeq = (response.json \\ "email")
promiseOfAddresses.success(emailsSeq) // here you will complete promise with actual value
}
val lb = promiseOfAddresses.future.get // here you will obtain future from promise and also get value of future. Method get is blocking and it will wait till promise is fullfiled or failed
lb.foreach(println) //This is empty
lb
}
PS2: Probably best help with future/promises can be found here: the-neophytes-guide-to-scala-part-9-promises-and-futures-in-practice (I have not enough reputation, so google this blog) It is something like CompletableFuture in java world. But remember, that best way is to stay all time in red side (red = asynchronous functions/methods. see nice, but chatty, blog what-color-is-your-function)

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