Inject user index into body file in Gatling scenario - gatling

I want to use ELFileBody and put a variable in a txt file.
This file contains a soap request.
The request (scenario) is executed only one time but as many times as users.
I want to put into file variable, the user index (position in execution).
Something like this :
.set("myVar", userIndex) //myVar is the variable declared in the body file ( ${myVar} )
Here is my code for now :
val users = 1500
val baseUrl = "http://127.0.0.1:7001"
val httpProtocol = http
.baseURL(baseUrl)
.inferHtmlResources()
.acceptEncodingHeader("gzip,deflate")
.contentTypeHeader("text/xml;charset=UTF-8")
.userAgentHeader("Apache-HttpClient/4.1.1 (java 1.5)")
val headers_0 = Map("SOAPAction" -> """""""")
val uri1 = "http://127.0.0.1:7001/myProject-ws/myProjectWebService"
val scn = scenario("Scenario1Name")
.exec(http("scn.Scenario1Name")
.post("/myProject-ws/myProjectWebService")
.headers(headers_0)
.body(RawFileBody("File_0000_request.txt")))
setUp(scn.inject(atOnceUsers(users))).protocols(httpProtocol)
How can I inject the user index into myVar variable in the request body ?

finally, i used a function that return a dynamic reference (id) and I call it from my scenario.
def getDynamicId(): String = {
val formatter = new SimpleDateFormat("yyyyMMddHHmmss.SSS")
val result = "PM".concat(formatter.format(Calendar.getInstance().getTime()))
result
}
//[...]
scenario("ScenarioName")
.exec(session => session.set("myVar", getDynamicId))
// [...]
.body(ElFileBody("BodyFile_0000_request.txt")))
And in the body file, I have the variable ${myVar}

You need read file like e.g.
val customSeparatorFeeder = separatedValues(pathToFile, separator).queue circular
after scenario("Scenario1Name") you need to add .feed(customSeparatorFeeder)
you can read more about it here https://gatling.io/docs/2.3/session/feeder/

Related

Kotlin Parsing json array with new line separator

I'm using OKHttpClient in a Kotlin app to post a file to an API that gets processed. While the process is running the API is sending back messages to keep the connection alive until the result has been completed. So I'm receiving the following (this is what is printed out to the console using println())
{"status":"IN_PROGRESS","transcript":null,"error":null}
{"status":"IN_PROGRESS","transcript":null,"error":null}
{"status":"IN_PROGRESS","transcript":null,"error":null}
{"status":"DONE","transcript":"Hello, world.","error":null}
Which I believe is being separated by a new line character, not a comma.
I figured out how to extract the data by doing the following but is there a more technically correct way to transform this? I got it working with this but it seems error-prone to me.
data class Status (status : String?, transcript : String?, error : String?)
val myClient = OkHttpClient ().newBuilder ().build ()
val myBody = MultipartBody.Builder ().build () // plus some stuff
val myRequest = Request.Builder ().url ("localhost:8090").method ("POST", myBody).build ()
val myResponse = myClient.newCall (myRequest).execute ()
val myString = myResponse.body?.string ()
val myJsonString = "[${myString!!.replace ("}", "},")}]".replace (",]", "]")
// Forces the response from "{key:value}{key:value}"
// into a readable json format "[{key:value},{key:value},{key:value}]"
// but hoping there is a more technically sound way of doing this
val myTranscriptions = gson.fromJson (myJsonString, Array<Status>::class.java)
An alternative to your solution would be to use a JsonReader in lenient mode. This allows parsing JSON which does not strictly comply with the specification, such as in your case multiple top level values. It also makes other aspects of parsing lenient, but maybe that is acceptable for your use case.
You could then use a single JsonReader wrapping the response stream, repeatedly call Gson.fromJson and collect the deserialized objects in a list yourself. For example:
val gson = GsonBuilder().setLenient().create()
val myTranscriptions = myResponse.body!!.use {
val jsonReader = JsonReader(it.charStream())
jsonReader.isLenient = true
val transcriptions = mutableListOf<Status>()
while (jsonReader.peek() != JsonToken.END_DOCUMENT) {
transcriptions.add(gson.fromJson(jsonReader, Status::class.java))
}
transcriptions
}
Though, if the server continously provides status updates until processing is done, then maybe it would make more sense to directly process the parsed status instead of collecting them all in a list before processing them.

How to store array values in an environment variable in Postman

I am using a postman to automate apis.
Now I am using following request , lets say :-
{
"customerId": "{{currentClientId}}"
}
Where clientid is a dynamic variable whose value is substituted dynamically as 1 , 2, 3,4 so on..
I call this request multiple times using setNextRequest call in this eg lets say 10.This is being done using a counter variable. I am initialising the counter in my previous request to 0 and using for loop with value as counter as 10 calling the request 10 times.There is no response in body just successful http code 204.
I want to store all these clientids coming in request into an environment Client array variable so I wrote a following pre-request script:-
counter = pm.environment.get("counter");
ClientArray = pm.environment.get("ClientArray");
ClientArray.push(pm.environment.get("currentClientId"));
pm.environment.set("ClientArray",ClientArray);
In Test Script, wrote following code :-
counter = pm.environment.get("counter");
if(counter<=10) {
console.log("hi");
postman.setNextRequest("Request");
counter++;
pm.environment.set("counter",counter);
console.log("Counter",counter);
}
The above scipts is throwing
TypeError | ClientArray.push is not a function.
Could someone please advice how to achieve this.
When you retrieve a value from an environment variable like you're doing:
ClientArray = pm.environment.get("ClientArray");
You're not getting an array, you're getting a string which is why you're getting that error. You need to treat the variable like a string, append the currentClientId much like you do for the counter. Something like:
var currentClientIds = pm.environment.get("ClientArray");
currentClientIds = currentClientIds + "," + currentClientId
When you're done appending i.e. out of your loop simply take the string and convert it to an array:
var currentClientIds = pm.environment.get("ClientArray");
var idsArr = curentClientIds.split(',');

Gatling2 Failing to use user session properly

I hope someone can point me into the right direction!
I try to run one scenario which has several steps that have to be executed in order and each with the same user session to work properly. The below code works fine with one user but fails if I use 2 or more users...
What am I doing wrong?
val headers = Map(
Constants.TENANT_HEADER -> tenant
)
val httpConf = http
.baseURL(baseUrl)
.headers(headers)
val scen = scenario("Default Order Process Perf Test")
.exec(OAuth.getOAuthToken(clientId))
.exec(session => OAuth.createAuthHHeader(session, clientId))
.exec(RegisterCustomer.registerCustomer(customerMail, customerPassword,
tenant))
.exec(SSO.doLogin(clientId, customerMail, customerPassword, tenant))
.exec(session => OAuth.upDateAuthToken(session, clientId))
.exec(session =>
UpdateCustomerBillingAddr.prepareBillingAddrRequestBody(session))
.exec(UpdateCustomerBillingAddr.updateCustomerBillingAddr(tenant))
.exec(RegisterSepa.startRegisterProcess(tenant))
.exec(session => RegisterSepa.prepareRegisterRequestBody(session))
.exec(RegisterSepa.doRegisterSepa(tenant))
setUp(
scen
.inject(atOnceUsers(2))
.protocols(httpConf))
object OAuth {
private val OBJECT_MAPPER = new ObjectMapper()
def getOAuthToken(clientId: String) = {
val authCode = PropertyUtil.getAuthCode
val encryptedAuthCode = new
Crypto().rsaServerKeyEncrypt(authCode)
http("oauthTokenRequest")
.post("/oauth/token")
.formParam("refresh_token", "")
.formParam("code", encryptedAuthCode)
.formParam("grant_type", "authorization_code")
.formParam("client_id", clientId)
.check(jsonPath("$").saveAs("oauthToken"))
.check(status.is(200))
}
def createAuthHHeader(session: Session, clientId: String) = {
val tokenString = session.get("oauthToken").as[String]
val tokenDto = OBJECT_MAPPER.readValue(tokenString,
classOf[TokenDto])
val session2 = session.set(Constants.TOKEN_DTO_KEY, tokenDto)
val authHeader = AuthCommons.createAuthHeader(tokenDto,
clientId, new util.HashMap[String, String]())
session2.set(Constants.AUTH_HEADER_KEY, authHeader)
}
def upDateAuthToken(session: Session, clientId: String) = {
val ssoToken = session.get(Constants.SSO_TOKEN_KEY).as[String]
val oAuthDto = session.get(Constants.TOKEN_DTO_KEY).as[TokenDto]
val params = new util.HashMap[String, String]
params.put("sso_token", ssoToken)
val updatedAuthHeader = AuthCommons.createAuthHeader(oAuthDto,
clientId, params)
session.set(Constants.AUTH_HEADER_KEY, updatedAuthHeader)
}
}
def createAuthHHeader(session: Session, clientId: String) = {
val tokenString = session.get("oauthToken").as[String]
val tokenDto = OBJECT_MAPPER.readValue(tokenString,
classOf[TokenDto])
val session2 = session.set(Constants.TOKEN_DTO_KEY, tokenDto)
val authHeader = AuthCommons.createAuthHeader(tokenDto,
clientId, new util.HashMap[String, String]())
session2.set(Constants.AUTH_HEADER_KEY, authHeader)
}
So I did add the two methods that dont work along as expected. In the first part I try to fetch a token and store in the session via check(jsonPath("$").saveAs("oauthToken")) and in the second call I try to read that token with val tokenString = session.get("oauthToken").as[String] which fails with the exception saying that there is no entry for that key in the session...
I've copied it and removed/mocked any missing code references, switched to one of my apps auth url and it seems to work - at least 2 firsts steps.
One thing that seems weird is jsonPath("$").saveAs("oauthToken") which saves whole json (not single field) as attribute, is it really what you want to do? And are you sure that getOAuthToken is working properly?
You said that it works for 1 user but fails for 2. Aren't there any more errors? For debug I suggest changing logging level to TRACE or add exec(session => {println(session); session}) before second step to verify if token is properly saved to session. I think that something is wrong with authorization request (or building that request) and somehow it fails or throws some exception. I would comment out all steps except 1st and focus on checking if that first request is properly executed and if it adds proper attribute to session.
I think your brackets are not set correctly. Change them to this:
setUp(
scn.inject(atOnceUsers(2))
).protocols(httpConf)

How to increment a variable in Gatlling Loop

I am trying to write a Gatling script where I read a starting number from a CSV file and loop through, say 10 times. In each iteration, I want to increment the value of the parameter.
It looks like some Scala or Java math is needed but could not find information on how to do it or how and where to combine Gatling EL with Scala or Java.
Appreciate any help or direction.
var numloop = new java.util.concurrent.atomic.AtomicInteger(0)
val scn = scenario("Scenario Name")
.asLongAs(_=> numloop.getAndIncrement() <3, exitASAP = false){
feed(csv("ids.csv")) //read ${ID} from the file
.exec(http("request")
.get("""http://finance.yahoo.com/q?s=${ID}""")
.headers(headers_1))
.pause(284 milliseconds)
//How to increment ID for the next iteration and pass in the .get method?
}
You copy-pasted this code from Gatling's Google Group but this use case was very specific.
Did you first properly read the documentation regarding loops? What's your use case and how doesn't it fit with basic loops?
Edit: So the question is: how do I get a unique id per loop iteration and per virtual user?
You can compute one for the loop index and a virtual user id. Session already has a unique ID but it's a String UUID, so it's not very convenient for what you want to do.
// first, let's build a Feeder that set an numeric id:
val userIdFeeder = Iterator.from(0).map(i => Map("userId" -> i))
val iterations = 1000
// set this userId to every virtual user
feed(userIdFeeder)
// loop and define the loop index
.repeat(iterations, "index") {
// set an new attribute named "id"
exec{ session =>
val userId = session("userId").as[Int]
val index = session("index").as[Int]
val id = iterations * userId + index
session.set("id", id)
}
// use id attribute, for example with EL ${id}
}
Here is my answer to this:
Problem Statement: I had to repeat the gatling execution for configured set of times, and my step name has to be dynamic.
object UrlVerifier {
val count = new java.util.concurrent.atomic.AtomicInteger(0)
val baseUrl = Params.applicationBaseUrl
val accessUrl = repeat(Params.noOfPagesToBeVisited,"index") {
exec(session=> {
val randomUrls: List[String] = UrlFeeder.getUrlsToBeTested()
session.set("index", count.getAndIncrement).set("pageToTest", randomUrls(session("index").as[Int]))
}
).
exec(http("Accessing Page ${pageToTest}")
.get(baseUrl+"${pageToTest}")
.check(status.is(200))).pause(Params.timeToPauseInSeconds)
}
So basically UrlFeeder give me list of String (urls to be tested) and in the exec, we are using count (AtomicInteger), and using this we are populating a variable named 'index' whose value will start from 0 and will be getAndIncremented in each iteration. This 'index' variable is the one which will be used within repeat() loop as we are specifying the name of counterVariable to be used as 'index'
Hope it helps others as well.

One Minute Man, python 3

Well, i have this code that is supposed to check if the html is changed by first checking and downloading the html into a string, then checking again every two seconds and printing html if it has changed. The problem is that the script says it has changed all the time, and keeps giving me the same html code back.
#!/usr/bin/env python
import time
start = time.time()
from urllib.request import urlopen
data = str
html = str
def firstcheck():
url = 'http://www.hacker.org/challenge/misc/minuteman.php'
hogniergay = urlopen(url)
data = hogniergay.read()
hogniergay.close()
html = data
def secondcheck():
url = 'http://www.hacker.org/challenge/misc/minuteman.php'
hogniergay = urlopen(url)
data = hogniergay.read()
hogniergay.close()
if not html == data:
print(data)
while True:
secondcheck()
time.sleep(2)
print ("it took", time.time() - start, "seconds.")
Thanks in advance;)
You need to tell the interpreter to set the global html variable in the firstcheck() function.
def firstcheck():
url = 'http://www.hacker.org/challenge/misc/minuteman.php'
hogniergay = urlopen(url)
data = hogniergay.read()
hogniergay.close()
global html
html = data
Right now the secondcheck() function is checking against the html value "str".
It doesn't look like you are calling firstcheck at all, so html is always going to be str. You could make it work by replacing the block inside the while True with:
while True:
firstcheck()
secondcheck()
but it would be cleaner to have a script that looked something like this
while True:
hogniergay = urlopen(url)
result = hogniergay.read()
hogniergay.close()
if result != current:
print (result)
current = result
time.sleep(2)

Resources