Value for a session is replicated between sessions - scala

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()}"))

Related

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

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}")

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}")

Play FakeRequest with Specs2 remembers Request across tests

I have a small Play (2.1.2) application that tries to store some data and perform a redirect. I have 2 specs:
"save the user" in {
running(FakeApplication()) {
val Some(create) = route(
FakeRequest(PUT, "/users")
.withSession(("user-id", user_id))
.withFormUrlEncodedBody(("username", any_username))
)
status(create) must equalTo(SEE_OTHER)
redirectLocation(create).map(_ must equalTo("/profile")) getOrElse failure("missing redirect location")
}
}
"display errors with missing username" in {
running(FakeApplication()) {
val Some(create) = route(
FakeRequest(PUT, "/users")
.withSession(("user-id", user_id))
)
status(create) must equalTo(BAD_REQUEST)
contentAsString(create) must contain ("This field is required")
}
}
When I run these tests, the second test has the same result as the first one, so a SEE_OTHER instead of the BAD_REQUEST. When I change the order of the tests, both work fine. The second one also passes when I delete the first one.
Does Scala / Play / Specs2 somehow remember state across tests or requests? Is there anything I need to do to ensure they run in isolation?
EDIT:
The code in my controller looks like this:
val form: Form[User] = Form(
mapping(
"username" -> nonEmptyText
)(user => User(username))(user=> Some(user.username))
)
form.bindFromRequest.fold(
errors => BadRequest(views.html.signup(errors)),
user => Redirect("/profile")
)
Playframework 2/Specs2 does not keep state between tests unless you keep state in your test classes, your application or any external place you keep data.
If for example your application would save a user to a database in one test and test for the existance of that user in another test then of course that would make your test interfere with each other.
So I guess you need to figure out some way to clean out the database where you keep your data between each test.

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