How to create a load splitting scenario? in JMeter - jmeter

Currently i'm working on load splitting scenario in jmeter.
My test-plan structure is similar as below:
TestPlan
BeanShell Preprocessor (here I have below code)
Integer totalLoad = 0;
Integer searchUsers = 0;
Integer createUseres = 0;
totalLoad = Integer.parseInt (vars.get("TOTALUSERS"));
searchUsers = ((totalLoad / 100) * 40); createUseres =((totalLoad / 100) * 10); guestUsers = ((totalLoad / 100) * 50);
vars.put("searchUsers", searchUsers.toString());
vars.put("createUseres", createUseres.toString());
vars.put("guestUsers", guestUsers.toString());
Thread group: Search (I am using ${searchUsers} in the number of Threads(users) field)
Thread group: Create (I am using ${createUseres} in the number of Threads(users) field)
Thread group: Guest (I am using ${guestUsers} in the number of Threads(users) field)
This is not working for me.
Can you please help?

JMeter variables are local to each thread. So if you'll create those variables in preprocessor, they won't be accessible to other threads at this level.
You should use properties to put/get searchUsers, createUseres, guestUsers like:
Use props.put("guestUsers", guestUsers.toString()); in BeanShell Preprocessor. Use ${__P(searchUsers)} as number of threads in Thread Group.

Related

Gatling - Dynamic Scenario, InjectionProfile, Assertion creation based on Configuration

I am attempting to write a simulation that can read from a config file for a set of apis that each have a set of properties.
I read the config for n active scenarios and create requests from a CommonRequest class
Then those requests are built into scenarios from a CommonScenario
CommonScenarios have attributes that are using to create their injection profiles
That all seems to work no issue. But when I try to use the properties / CommonScenario request to build a set of Assertions it does not work as expected.
// get active scenarios from the config
val activeApiScenarios: List[String] = Utils.getStringListProperty("my.active_scenarios")
// build all active scenarios from config
var activeScenarios: Set[CommonScenario] = Set[CommonScenario]()
activeApiScenarios.foreach { scenario =>
activeScenarios += CommonScenarioBuilder()
.withRequestName(Utils.getProperty("my." + scenario + ".request_name"))
.withRegion(Utils.getProperty("my." + scenario + ".region"))
.withConstQps(Utils.getDoubleProperty("my." + scenario + ".const_qps"))
.withStartQps(Utils.getDoubleListProperty("my." + scenario + ".toth_qps").head)
.withPeakQps(Utils.getDoubleListProperty("my." + scenario + ".toth_qps")(1))
.withEndQps(Utils.getDoubleListProperty("my." + scenario + ".toth_qps")(2))
.withFeeder(Utils.getProperty("my." + scenario + ".feeder"))
.withAssertionP99(Utils.getDoubleProperty("my." + scenario + ".p99_lte_assertion"))
.build
}
// build population builder set by adding inject profile values to scenarios
var injectScenarios: Set[PopulationBuilder] = Set[PopulationBuilder]()
var assertions : Set[Assertion] = Set[Assertion]()
activeScenarios.foreach { scenario =>
// create injection profiles from CommonScenarios
injectScenarios += scenario.getCommonScenarioBuilder
.inject(nothingFor(5 seconds),
rampUsersPerSec(scenario.startQps).to(scenario.rampUpQps).during(rampOne seconds),
rampUsersPerSec(scenario.rampUpQps).to(scenario.peakQps).during(rampTwo seconds),
rampUsersPerSec(scenario.peakQps).to(scenario.rampDownQps) during (rampTwo seconds),
rampUsersPerSec(scenario.rampDownQps).to(scenario.endQps).during(rampOne seconds)).protocols(httpProtocol)
// create scenario assertions this does not work for some reason
assertions += Assertion(Details(List(scenario.requestName)), TimeTarget(ResponseTime, Percentiles(4)), Lte(scenario.assertionP99))
}
setUp(injectScenarios.toList)
.assertions(assertions)
Note scenario.requestName is straight from the build scenario
.feed(feederBuilder)
.exec(commonRequest)
I would expect the Assertions get built from their scenarios into an iterable and pass into setUp().
What I get:
When I print out everything the scenarios, injects all look good but then I print my "assertions" and get 4 assertions for the same scenario name with 4 different Lte() values. This is generalized but I configured 12 apis all with different names and Lte() values, etc.
Details(List(Request Name)) - TimeTarget(ResponseTime,Percentiles(4.0)) - Lte(500.0)
Details(List(Request Name)) - TimeTarget(ResponseTime,Percentiles(4.0)) - Lte(1500.0)
Details(List(Request Name)) - TimeTarget(ResponseTime,Percentiles(4.0)) - Lte(1000.0)
Details(List(Request Name)) - TimeTarget(ResponseTime,Percentiles(4.0)) - Lte(2000.0)
After the simulation the assertions all run like normal:
Request Name: 4th percentile of response time is less than or equal to 500.0 : false
Request Name: 4th percentile of response time is less than or equal to 1500.0 : false
Request Name: 4th percentile of response time is less than or equal to 1000.0 : false
Request Name: 4th percentile of response time is less than or equal to 2000.0 : false
Not sure what I am doing wrong when building my assertions. Is this even a valid approach? I wanted to ask for help before I abandon this for a different approach.
Disclaimer: Gatling creator here.
It should work.
Then, there are several things I'm super not found of.
assertions += Assertion(Details(List(scenario.requestName)), TimeTarget(ResponseTime, Percentiles(4)), Lte(scenario.assertionP99))
You shouldn't be using the internal AST here. You should use the DSL like you've done for the injection profile.
var assertions : Set[Assertion] = SetAssertion
activeScenarios.foreach { scenario =>
You should use map on activeScenarios (similar to Java's Stream API), not use a mutable accumulator.
val activeScenarios = activeApiScenarios.map(???)
val injectScenarios = activeScenarios.map(???)
val assertions = activeScenarios.map(???)
Also, as you seem to not be familiar with Scala, you should maybe switch to Java (supported for more than 1 year).

How to modify the parameters of jmeter at runtime? such as the number of thread pools

I'm using the jmeter api to develop pressure testing tools,
How to modify the parameters of jmeter at runtime? such as the number of thread pools,
ConstantThroughputTimer.throughput
demo
github,but not found answer
You cannot change the number of threads in the runtime (at least not with JMeter 5.5)
What you can do is to use Constant Throughput Timer in combination with Beanshell Server to control requests execution rate.
I tried and found the answer by writing my own code. Parameters can be dynamically modified in the form of apis. Just call JMeterUtils.getJMeterProperties().setProperty("throughput", prop)。
ConstantThroughputTimer :
ConstantThroughputTimer timer = new ConstantThroughputTimer();
long rpsCalc = (long) (rps * 60);
String paramStr = "${__P(throughput,50)}";
timer.setProperty("calcMode", 2);
StringProperty stringProperty = new StringProperty();
stringProperty.setName("throughput");
stringProperty.setValue(paramStr);
timer.setProperty(stringProperty);
timer.setEnabled(true);
timer.setProperty(TestElement.TEST_CLASS, ConstantThroughputTimer.class.getName());
timer.setProperty(TestElement.GUI_CLASS, TestBeanGUI.class.getName());
return timer;

How to execute an API with multiple sets of users in parallel using JMeter

I have a CSV file which contains 500 users.
Set1 1-100
Set2 100-200
Set3 300-400
Set4 400-500
I need to execute an API with above 4 set of users in parallel.
One option is to create 5 thread groups with 5 files(each file with set) but this is tedious process if set increases to 20-30. Need to maintain 20-30 files.
Is there any plugin or way using which we can execute API with 4 sets of users in parallel ?
I fail to see why would you need 4 thread groups for this, it would be sufficient to go for a single Thread Group
If you don't need the set of users from 200 to 300 it can be removed from the original CSV file in setUp Thread Group using JSR223 Sampler and the following Groovy code:
def originalLines = new File('original.csv').readLines()
def linesFrom0To200 = originalLines.take(200)
def linesfrom300To500 = originalLines.takeRight(200)
def newLines = linesFrom0To200 + linesfrom300To500
new File('new.csv').withWriter { out ->
newLines.each { out.println it }
}
This code will take original.csv, remove lines from 201 to 299 inclusively and write the result into new.csv file.
You can use this file in the CSV Data Set Config in the "normal" Thread Group and each thread (virtual user) will take next line on each iteration

thread Local Cached Connection

I was using JMeter properties for storing the threadLocalCachedConnection object. I made sure to use unique property names as properties.
In thread group 1, I had a JSR223 PostProcessor to scrape session per thread(VU), and then store it in a property called sessionID.
And I added another JSR223 PostProcessor as a child of the last sampler in the Thread Group1.
def connection = sampler.threadLocalCachedConnection
props.put("presenterConnection" + ctx.getThreadNum(), connection.get())
In Thread Group 2, I added a JSR223 PreProcessor as a child of the first sampler.
def presenterConnection = props.get('presenterConnection' + ctx.getThreadNum())
sampler.threadLocalCachedConnection.set(presenterConnection)
String sendCommand = "SEND\n" +
"content-type:application/json;charset=UTF-8\n" +
"destination:/v1/session/${__property(sessionId)}/command\n" +
"id:perftest01-presenter-${__property(sessionId)}\n" +
"\n" +
"{\"type\": \"go-to-slide\", \"data\": {\"index\": 0}}\n" +
'\0' // note: NULL char at end
;
vars.put("wsStompSendCommand", sendCommand);
I tested with 2 threads (VUs). Why both threads were using the last sessionId instead of using one sessionId per thread??
As per JMeter Documentation:
Properties are not the same as variables. Variables are local to a thread; properties are common to all threads
so your line
props.put('sessionId', vars.get('sessionUUID'))
creates a global sessionId propety which is:
common for all Threads no matter in which Thread Group they are
exists until you shut down JMeter/JVM
You need to play the same trick as with the presenterConnection to wit:
props.put('sessionId_'+ ctx.getThreadNum(), vars.get('sessionUUID'))
and then read it where required:
def sessionId = props.get('sessionId_'+ ctx.getThreadNum())
More information: Apache Groovy: What Is Groovy Used For?

JMeter, threads using dynamic incremented value

I have created a JMeter functional test that essentially:
creates a user;
logs in with the user;
deletes the user.
Now, I need to be able to thread this, and dynamically generate a username with a default prefix and a numerically incremented suffix (ie TestUser_1, TestUser_2, ... etc).
I used the counter, and things were working fine until I really punched up the number of threads/loops. When I did this, I was getting a conflict with the counter. Some threads were trying to read the counter, but the counter had already been incremented by another thread. This resulted in trying to delete a thread that was just created, then trying to log in with a thread that was just deleted.
The project is set up like this:
Test Plan
Thread group
Counter
User Defined Variables
Samplers
I was hoping to solve this problem by using the counter to append a number to the user defined variables upon thread execution, but the counter cannot be accessed in the user defined variables.
Any ideas on how I can solve this problem?
Thank you in advance.
I've used the following scheme successfully with any amount of test users:
1. Generate using beanshell-script (in BeanShell Sampler e.g.) csv-file with test-user details, for example:
testUserName001,testPwd001
testUserName002,testPwd002
. . .
testUserName00N,testPwd00N
with the amount of entries you need for the test-run.
This is done once per "N users test-run", in separate Thread Group, in setUp Thread Group or maybe even in separate jmx-script... makes no difference.
You can please find working beanshell-code below.
2. Create your test users IN TEST APPLICATION using previously created users-list.
If you don't need create in application you may skip this.
Thread Group
Number of Threads = 1
Loop Count = 1
. . .
While Controller
Condition = ${__javaScript("${newUserName}"!="",)} // this will repeat until EOF
CSV Data Set Config
Filename = ${__property(user.dir)}${__BeanShell(File.separator,)}${__P(users-list,)} // path to generated users-list
Variable Names = newUserName,newUserPwd // these are test-users details read from file into pointed variables
Delimiter = '
Recycle on EOF? = False
Stop thread on EOF? = True
Sharing Mode = Current thread group
[CREATE TEST USERS LOGIC HERE] // here are actions to create separate user in application
. . .
3. Perform multi-user logic.
Schema like the given above one but Thread Group executed not for 1 but for N threads.
Thread Group
Number of Threads = ${__P(usersCount,)} // set number of users you need to test
Ramp-Up Period = ${__P(rampUpPeriod,)}
Loop Count = X
. . .
While Controller
Condition = ${__javaScript("${newUserName}"!="",)} // this will repeat until EOF
CSV Data Set Config
Filename = ${__property(user.dir)}${__BeanShell(File.separator,)}${__P(users-list,)} // path to generated users-list
Variable Names = newUserName,newUserPwd // these are test-users details read from file into pointed variables
Delimiter = '
Recycle on EOF? = False
Stop thread on EOF? = True
Sharing Mode = Current thread group
[TEST LOGIC HERE] // here are test actions
. . .
The key idea is in using Thread Group + While Controller + CSV Data Set Config combination:
3.1. CSV Data Set Config reads details for each the test users from generated file:
. . . a. only once - because of "Stop thread on EOF? = True";
. . . b. doesn't block file for further access (in another thread groups, e.g., if there are any) - because of "Sharing Mode = Current thread group";
. . . c. pointed variables - "Variable Names = newUserName,newUserPwd" - you will use in further test-actions;
3.2. While Controller forces CSV Data Set Config to read all the entries from generated file - because of defined condition (until the EOF).
3.3. Thread Group will start all the threads with defined ramp-up - or simultaneously if ramp-up = 0.
You can take here template script for described schema: multiuser.jmx.
Beanshell script to generate test-users details looks like below and takes the following args:
- test-users count
- test-user name template ("TestUser_" in your case)
- test-user name format (e.g. 0 - to get TestUser_1, 00 - to get TestUser_01, 000- for TestUser_001, etc... you can simply hardcode this orexclude at all)
- name of generated file.
import java.text.*;
import java.io.*;
import java.util.*;
String [] params = Parameters.split(",");
int count = Integer.valueOf(params[0]);
String testName = params[1];
String nameFormat = params[2];
String usersList = params[3];
StringBuilder contents = new StringBuilder();
try {
DecimalFormat formatter = new DecimalFormat(nameFormat);
FileOutputStream fos = new FileOutputStream(System.getProperty("user.dir") + File.separator + usersList);
for (int i = 1; i <= count; i++) {
String s = formatter.format(i);
String testUser = testName + s;
contents.append(testUser).append(",").append(testUser);
if (i < count) {
contents.append("\n");
}
}
byte [] buffer = contents.toString().getBytes();
fos.write(buffer);
fos.close();
}
catch (Exception ex) {
IsSuccess = false;
log.error(ex.getMessage());
System.err.println(ex.getMessage());
}
catch (Throwable thex) {
System.err.println(thex.getMessage());
}
All together it will look like:
Sorry if answer is too overloaded.
Hope this helps.
The "User Defined Variables" config element does not pick up the reference variable from the "Counter" config element. I think this is a bug in JMeter. I have verified this behavior in version 3.2.
I added a "BeanShell Sampler" element to work around the issue.
Notice that the reference name of the "Counter" element is INDEX
The RUN_FOLDER variable gets set to a combination of the TESTS_FOLDER variable and the INDEX variable in the "BeanShell Sampler"
The "Debug Sampler" simply gathers a snapshot of the variables so I can see them in the "View Results Tree" listener element. Notice how the RUN_FOLDER variable has the INDEX variable value (5 in this case) appended.

Resources