Get start time, end time and test duration of JMeter load test - jmeter

I need to capture StartTime(Format:2021-04-28T12:09:09.000009), EndTime(Format:2021-04-28T12:09:09.000009) and test duration(milliseconds) of a JMeterLoad test.
I planning to capture StartTime(Format:2021-04-28T12:09:09.000009) in SetupThreadGroup in Jsr223Sampler and EndTime(Format:2021-04-28T12:09:09.000009) & duration in tearDownThreadGroup using below code.
---SetupThread
def now = new Date()
def starttime = now.format("yyyy-MM-dd'T'HH:mm:ss.ssssss", TimeZone.getTimeZone('UTC'))
vars.put("StartTime", starttime)
--- TearDownThreadGroup
def now = new Date()
def endtime = now.format("yyyy-MM-dd'T'HH:mm:ss.ssssss", TimeZone.getTimeZone('UTC'))
vars.put("EndTime", endtime)
def diff = vars.get("StartTime")-endtime
Getting below error
2021-04-28 18:17:11,475 ERROR o.a.j.p.j.s.JSR223Sampler: Problem in JSR223 script GetEndTime_Duration, message: javax.script.ScriptException: java.lang.NullPointerException: Cannot invoke method minus() on null object
javax.script.ScriptException: java.lang.NullPointerException: Cannot invoke method minus() on null object

You have start time saved already in variable TESTSTART.MS, you can use it in script, e.g.
long starttime = Long.valueOf(vars.get("TESTSTART.MS"))
def now = new Date()
def endtime = now.format("yyyy-MM-dd'T'HH:mm:ss.ssssss", TimeZone.getTimeZone('UTC'))
vars.put("EndTime", endtime)
def diff = now.getTime() - starttime
log.info String.valueOf(diff)

There are multiple problems with your script:
You're using vars shorthand which stands for JMeterVariables class instance and according to JMeter Documentation
Properties are not the same as variables. Variables are local to a thread; properties are common to all threads, and need to be referenced using the __P or __property function
so you should be using props instead of vars, see Top 8 JMeter Java Classes You Should Be Using with Groovy for more information on what do these shorthands mean
You're subtracting 2 strings
You're subtracting larger value (end time) from the lower value (start time)
My expectation is that in the setUp Thread Group you should use the following code:
def starttime = System.currentTimeMillis()
props.put("StartTime", starttime)
and in the tearDown Thread Group:
def endtime = System.currentTimeMillis()
def diff = endtime - props.get('StartTime')
log.info('Diff: ' + diff)

Related

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?

Can we retrieve a file name in run time in jmeter and pass the name in other place

During the jmeter run,a log file is created and stored in a location with timestamp. i want to refer the same filename in the next postprocessor. how do we achieve it
You want to get the name of the .jtl results file in JMeter script?
Something like:
?
If yes, you can use a suitable JSR223 Test Element and the following Groovy code:
import org.apache.jmeter.reporters.ResultCollector
import org.apache.jorphan.collections.SearchByClass
def engine = engine = ctx.getEngine()
def test = engine.getClass().getDeclaredField('test')
test.setAccessible(true)
def testPlanTree = test.get(engine)
SearchByClass<ResultCollector> resultCollectorSearch = new SearchByClass<>(ResultCollector.class)
testPlanTree.traverse(resultCollectorSearch)
ResultCollector resultCollector = resultCollectorSearch.getSearchResults().first()
println('Current .jtl file name: ' + resultCollector.getFilename())

Replace blank with date

String c = ${__timeShift(yyyy-MM-dd,,P10D,)}
log.info("Today date " + "${c}")
vars.put("DATE_PLUS_10", "${c}")
System.out.println("${c}"); ---- This is printing 2007
def response = prev.getResponseDataAsString()
log.info('Response Initial: ' + response)
def request = response.replaceAll('""', ${DATE_PLUS_10})
log.info('Response Massaged: ' + request)
vars.put('request', request)
//vars.putObject('request', 'request');
System.out.println(${request});
I need to add 10 days to current date and replace the null value with DATE_PLUS_10
2020-02-29 20:20:15,548 ERROR o.a.j.e.JSR223PostProcessor: Problem in
JSR223 script, RESPONSE_STORE javax.script.ScriptException:
groovy.lang.MissingMethodException: No signature of method:
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.$() is applicable
for argument types: (Script2$_run_closure1) values:
[Script2$_run_closure1#76fcaee5] Possible solutions:
is(java.lang.Object), any(), get(java.lang.String),
any(groovy.lang.Closure), use([Ljava.lang.Object;), wait() at
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:320)
~[groovy-all-2.4.13.jar:2.4.13] at
org.codehaus.groovy.jsr223.GroovyCompiledScript.eval(GroovyCompiledScript.java:72)
~[groovy-all-2.4.13.jar:2.4.13] at
javax.script.CompiledScript.eval(CompiledScript.java:92)
~[?:1.8.0_181]
Can someone pls tell me what is the issue in the line :
def request = response.replaceAll('""', ${DATE_PLUS_10})
String c = ${__timeShift(yyyy-MM-dd,,P10D,)} -
DATE_PLUS_10=2007 - this is what debug sampler displays..
Don't use ${} syntax in JSR223 script,
You can add a PreProcessor User Parameters
Allows the user to specify values for User Variables specific to individual threads
Add a variable as DATE_PLUS_10 with value:
${__timeShift(yyyy-MM-dd,,P10D,)}
Then use it in script
String c = vars.get("DATE_PLUS_10");
you can use a global or user variable and after that you can write
def DATE_PLUS_10=vars.get("DATE_PLUS_10")
log.info(DATE_PLUS_10)
def response = "2020-05-01"//prev.getResponseDataAsString()
log.info('Response Initial: ' + response)
def request = response.replaceAll('""', DATE_PLUS_10)
log.info('Response Massaged: ' + request)
As mentioned in previous answer you can not use ${} inside JSR223 processor.
I tried the below this beanshell preprocessor.
import java.text.SimpleDateFormat;
import java.util.Date;
Date date = new Date();
date.setDate(date.getDate()+10);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// or: SimpleDateFormat df = new SimpleDateFormat("MM-dd-yyyy");
String formattedDate = df.format(date);
vars.put("FUTUREDATE",formattedDate);
And used the JSR post processor
def mydate =vars.get("FUTUREDATE");
log.info("my date" + mydate)
def response = prev.getResponseDataAsString()
log.info('Response Initial: ' + response)
def request = response.replaceAll('""', mydate)
log.info('Response Massaged: ' + request)
vars.put('request', request)
//vars.putObject('request', 'request');
System.out.println(${request});
Issue resolved now.
Deon't inline JMeter Functions or Variables into scripts, you can use "Parameters" section instead like:
or alternatively if you want "pure" Groovy code:
def newDate = new Date().plus(10).format('yyyy-MM-dd')
More information:
Groovy Date.plus() function
Apache Groovy - Why and How You Should Use It

How to Duplicate file using JMeter

I am trying to duplicate a file using JMeter,
The scenario:
Loading a JSON file. For example, {"name":"John","age":"$age_place"}
Modify one property - age_place from 1 to 20
Save each modified iteration into a separate file
I have tried to do that with Simple Data Writer but it didn't work.
You have JSR223 Elements as Sampler or Pre processor which you can add powerful script,
The easiest is to replace age_place with your value, for example if saved in variable age_place:
f = new FileOutputStream("c:\\temp\\template.json", false);
p = new PrintStream(f);
this.interpreter.setOut(p);
print("{\"name\":\"John\",\"age\":\"age_place\"}".replaceAll("age_place", vars.get("age_place")));
f.close();
If you need to generate 20 files with different age you can do it using any of JSR223 Test Elements. Example Groovy code would look like:
def json = new groovy.json.JsonSlurper().parseText("{\"name\":\"John\",\"age\":\"\"}")
def builder = new groovy.json.JsonBuilder(json)
1.upto(20, {
builder.content.age= "${it}"
def writer = new File('file' + "${it}" + ".json").newWriter()
writer << builder.toPrettyString()
writer.close()
})
Once you execute your test it will create the following files in the "bin" folder of your JMeter installation:
file1.json - with the age of 1
file2.json - with the age of 2
...
file20.json - with the age of 20
References:
Groovy For Loop Examples
Groovy: Parsing and producing JSON
Apache Groovy - Why and How You Should Use It

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