How to clear header manager programmatically - jmeter

I have a TestPlan
Thread-group
HttpSampler
pre-processor
HttpHeaderManager[empty]
HttpRequestDefaults[empty]
Post-processor
I am using a pre-processor script to add headers dynamically to headerManager from reading a json file. it goes well .
import org.apache.jmeter.protocol.http.control.Header
int min = args[0].toInteger()
int max = args[1].toInteger()
int random = min + (int) (Math.random() * ((max - min) + 1));
// here 'inputjson' referring to slurped json object
inputjson.Headers.each{
it.each{ key,value -> sampler.getHeaderManager().add(new Header(key.replace('$random',random.toString()),value.replace('$random',(random+2).toString())));
}
}
the issue is, if a thread loop count is 3, then the set of headers are adding 3 times.
then I added a post processor script
sampler.getHeaderManager().clear()
This time initial[loop-1] run is going fine, next[loop-2] loop is a clear request with no headers. how can I achieve, each request will go with only 1 set of headers

Here is my working example - HeaderManager.clear() did not work, but removing the header by its name did its job.
import org.apache.jmeter.protocol.http.control.Header;
sampler.getHeaderManager().removeHeaderNamed("Authorization");
sampler.getHeaderManager().add(new Header("Authorization", "Bearer " + vars.get("token")));

You can use below code to remove headers programatically - first, getting the headers count and looping till the end of headers count.
int headers_size = sampler.getHeaderManager().size();
log.info("headers_size: "+ headers_size);
while(headers_size > 0) {
log.info("header to be removed:"+ sampler.getHeaderManager().get(0));
sampler.getHeaderManager().remove(0);
headers_size = sampler.getHeaderManager().size();
}

This will done by
adding sampler.setHeaderManager(new HeaderManager()) just before the headerManger add section .
each time a new HeaderManger will get added and will be used. Not sure it is the best solution , but its a working solution.

Don't use the f$%$ing method HeaderManager.clear() which existence I still don't get, and do not do that in a post-processor, better do this in your preprocessor before adding headers to Header Manager.
headerMgr = sampler.getHeaderManager();
while(headerMgr.getHeaders().iterator().hasNext()) {
headerName = headerMgr.getHeaders().iterator().next().getStringValue().split("\\s+")[0];
headerMgr.removeHeaderNamed(headerName);
}

Related

HTTP request based on timestamp using Jmeter

I am trying to send HTTP requests using jmeter for which I am using a HTTP sampler. The http requests have a parameter TaskID and these parameters read from a CSV file. I just wanted to make changes on how the HTTP request will be send.
The CSV file looks like this
Time TaskID
9000 42353456
9000 53463464
9000 65475787
9300 42354366
9300 23423535
9600 43545756
9600 53463467
9600 23435346
Now I want to send request based on the Time. For example in Time 9000 there are 3 TaskID. So I want to send 3 HTTP requests with those TaskIDs at a time. Similarly for the other Times as well. Any idea on how to do it?
Update:
I created a minimal working example for one possible solution.
Basically I read the csv in a JSR223 Sampler and group it with following groovy code in "read csv" sampler:
import org.apache.jmeter.services.FileServer
current_dir = FileServer.getFileServer().getBaseDir().replace("\\","/")
csv_lines = new File(current_dir + "/test.csv").readLines()
times = []
csv_lines.each { line ->
line = line.split(",")
time = line[0]
task_id = line[1]
if (vars.getObject(time)){
tasks = vars.getObject(time)
tasks.add(task_id)
vars.putObject(time, tasks)
}
else{
times.add(time)
vars.putObject(time, [task_id])
}
}
times.eachWithIndex { time, i ->
vars.put("time_" + (i+1), time)
}
Notes:
(i+1) is used because the ForEach Controller will not consider the 0th element
I used "," as csv separator and omitted the header line
the "initialize task_ids" sampler holds following code:
.
time = vars.get("time")
tasks = vars.getObject(time)
tasks.eachWithIndex {task, i ->
vars.put(time + "_" + (i+1), task)
}
I hope, this helps!

How to measure size of response data in JMeter

How to measure size of response data of multiple http samplers in JMeter. I need to find the overall size of all the responses not for individual responses. I am trying to fetch it through a Beanshell code but it displays the size of the last sample executed:-
import java.util.io.*;
import java.lang.io.*;
int totalsize;
test = prev.getResponseDataAsString().length();
log.info("size is = "+test);
totalsize = totalsize + test;
log.info("totalsize is = "+totalsize);
Thank you.
Use JSR223 code with props and set JMeter property totalsize with 0 at start
props.put("totalsize", Integer.parseInt(prop.get("totalsize")) + test);
Following solution also worked for me on a beanshell post processor:-
import java.util.io.*;
import java.lang.io.*;
test = prev.getResponseDataAsString().length();
log.info("size is = "+test);
text = ctx.getCurrentSampler().getName();
log.info("Sampler name is " +text);
if(text.equalsIgnoreCase("Test Sampler")){
props.put("totalsize",Integer.parseInt("0"));
}else{
props.put("totalsize", (props.get("totalsize")!=null?props.get("totalsize"):0) + test);
}
log.info("totalsize is = "+props.get("totalsize"));
"test" captures the size of each of the sample requests and keeps adding it to the "totalsize". At the end of the execution I am initializing totalsize back to 0.
it's more appropriate to use prev.getBytesAsLong() to get each sampler response size. Take a look at JMeter API
To have combined size of multiple responses you could try grouping needed requests in transaction via Transaction Controller.

Jmeter - How to check if less than 10 stylesheets are loading when I open a web page

I have a requirement where I need to verify that when I open a web page, there should less than 10 stylesheets and less than 20 .js are loading. Is there a way to do this in Jmeter?
It is, but you will need to do some scripting. Example solution:
Add Beanshell Assertion as a child of the HTTP Request you need to test.
Put the following code into the Beanshell Assertion "Script" area:
import org.apache.jmeter.samplers.SampleResult;
int js, css;
js = css = 0;
for (SampleResult subResult : SampleResult.getSubResults()) {
if (subResult.getUrlAsString().endsWith(".css")) {
css++;
} else if (subResult.getUrlAsString().endsWith(".js")) {
js++;
}
}
log.info("JS files: " + js); // you can comment or remove these lines
log.info("CSS files: " + css); // as they do nothing but print the numbers to jmeter.log
if (css > 10 || js > 20) {
Failure = true;
FailureMessage = "Exceeded maximum scripts/styles";
}
If any of the specified thresholds will be met, the sampler will get failed with the relevant message:
More information on conditionally failing JMeter tests: How to Use JMeter Assertions in Three Easy Steps

JMeter Thread Group Not Getting to BeanShell PostProcessor

In my JMeter test plan, I'm trying to write all errors out to a log. I'm using a BeanShell Post-Processor configured as follows
import org.apache.jmeter.services.FileServer;
if (ResponseCode != null && ResponseCode.equals("200") == false) {
Failure = true;
// displays in Results Tree
FailureMessage ="Creation of a new CAE record failed. Response code " + ResponseCode + "." ;
// Static elements
part1 = "Creation of a new record failed. Response code: ";
part2 = ". Sorry!";
// Open File(s)
FileOutputStream f = new FileOutputStream("d:\\error.csv", true);
PrintStream p = new PrintStream(f);
// Write data to file
p.println( part1 + ResponseCode + part2 );
// Close File(s)
p.close();
f.close();
}
I'm trying to do a simple test where the HTTP request is doing a POST that is passing in a json file from c:jmeter/tests/payloads where the directory no longer exists. (let's say someone accidentally deletes it...)
The issue is the test is stopping (see below) and never getting to the BeanShell to write the error out to a log file. I need to capture all error responses, and only error responses.
I'm not sure how to handle this. I've read Jmeter. BeanShell PostProcessor and others, but they doesn't address the issue of what happens when it doesn't get to the BeanShell.
Any help is appreciated!
org.apache.jorphan.util.JMeterStopThreadException: End of sequence
at org.apache.jmeter.functions.FileToString.execute(FileToString.java:105)
at org.apache.jmeter.engine.util.CompoundVariable.execute(CompoundVariable.java:142)
at org.apache.jmeter.engine.util.CompoundVariable.execute(CompoundVariable.java:118)
at org.apache.jmeter.testelement.property.FunctionProperty.getStringValue(FunctionProperty.java:101)
at org.apache.jmeter.testelement.AbstractTestElement.getPropertyAsString(AbstractTestElement.java:274)
at org.apache.jmeter.config.Argument.getValue(Argument.java:146)
at org.apache.jmeter.protocol.http.util.HTTPArgument.getEncodedValue(HTTPArgument.java:236)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.sendPostData(HTTPHC4Impl.java:1111)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.handleMethod(HTTPHC4Impl.java:453)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.sample(HTTPHC4Impl.java:329)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy.sample(HTTPSamplerProxy.java:74)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1146)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1135)
at org.apache.jmeter.threads.JMeterThread.process_sampler(JMeterThread.java:434)
at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:261)
at java.lang.Thread.run(Unknown Source)
Caused by: java.io.FileNotFoundException: File 'C:\JMeter\test\payloads\Request_1.json' does not exist
at org.apache.commons.io.FileUtils.openInputStream(FileUtils.java:299)
at org.apache.commons.io.FileUtils.readFileToString(FileUtils.java:1711)
at org.apache.commons.io.FileUtils.readFileToString(FileUtils.java:1734)
at org.apache.jmeter.functions.FileToString.execute(FileToString.java:102)
SOLUTION
Based on Dmitri's feedback, I've switched from a Beanshell PostProcessor to Beanshell Assertion. After some tweaking, I got it to work where it now writes only errors (response != 200) to an errors.csv file. Instead of appending the file from a previous run, it overwrites with each run so only the last run's errors are captured.
If anyone thinks my solution could be improved, I'd be happy to receive the feedback. Thanks again to Kiril and Dmitri.
import org.apache.jmeter.services.FileServer;
if (ResponseCode != null && ResponseCode.equals("200") == true) {
SampleResult.setResponseOK();
}
else if (!ResponseCode.equals ("200") == true ) {
Failure = true;
FailureMessage ="Creation of a new record failed. Response code " + ResponseCode + "." ; // displays in Results Tree
print ("Creation of a new record failed: Response code " + ResponseCode + "."); // goes to stdout
log.warn("Creation of a new record failed: Response code " + ResponseCode); // this goes to the JMeter log file
// Static elements or calculations
part1 = "Unable to generate a new record via POST. The response code is: \"";
part2 = "\". \n\n For response code = \'Non-HTTP ressponse\', verify the payload file still exists. \n For response code = 409, check the recordTypeId and recordGrpId combination for validity. \n For response code = 500, verify the database and its host server are reachable. ";
// Open File(s)
FileOutputStream f = new FileOutputStream(FileServer.getFileServer().getBaseDir() + "\\error.csv");
PrintStream p = new PrintStream(f);
// Write data to file
p.println( part1 + ResponseCode + part2 );
// Close File(s)
p.close();
f.close();
}
There are no ResponseCode, Failure and FailureMessage in the Beanshell PostProcessor, switch to Beanshell Assertion instead.
Your ResponseCode.equals("200") clause assumes successful response, error responses usually have response codes > 400
See How to Use BeanShell: JMeter's Favorite Built-in Component guide for comprehensive information on Beanshell scripting in JMeter.
Jmeter overwrites your error.csv file instead of appending to it because you reopen it on every assertion call. Try to open it beforeheand, e.g. in separate Beanshell Sampler in setup thread group:
file = new FileOutputStream("error.csv", true);
bsh.shared.custom_log = new PrintStream(file)
And then use it in your beanshell assertion in a way like:
if (ResponseCode.equals("200")==false) {
bsh.shared.custom_log.println( part1 + ResponseCode + part2 );
}
Btw, AFAIK, you didn't need this part at all, because http responses with code 200 are OK by default:
if (ResponseCode != null && ResponseCode.equals("200") == true) {
SampleResult.setResponseOK();
}
I did't tested the code so there might be typos, but very similar one works for me.
Beanshell shared values are accessed under lock, so beware of possible performance issues if you writes to it heavily. With script like this and fairly short strings (50-100 chars), i'v got ~1k writes per second without significant impact on jmeter perfomance.

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