JMeter Thread Group Not Getting to BeanShell PostProcessor - jmeter

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.

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!

Jmeter clear resultFile for each run

I have a test plan, where i have two thread groups, first thread group will run and will log the result into a file, the logged file will be the input to the second thread group.
Now, for doing this in thread group one i added BeanShell PostProcessor script as follows, but it works as follows
open jmeter and run the test plan (log file "testing.csv" will be created as fresh)
For first time "isHeaderWritten" property is false.
No without closing and reopening the jmeter, run the script again. i expect the log file "testing.csv" must be truncated and add the values freshly. But what acually happening is "testing.csv" file is appended with the new values.
Now, close the jmeter and open again, this time log file "testing.csv" will be truncated and fresh new values are added.
How to clear the file and re-write the records for each run (without closing the jemeter)
import org.apache.jmeter.util.JMeterUtils;
threadName=ctx.getThread().getThreadName();
log.info("threadName: "+threadName);
isHeaderWritten = JMeterUtils.getPropDefault("isHeaderWritten","false");
fileName="C:\\response\\testing.csv";
log.info("isHeaderWritten: "+isHeaderWritten);
if(isHeaderWritten == "true"){
f = new FileOutputStream(fileName, true);
fileHeader = "requestStatus,cNumber,pId,id,token";
}else{
log.error("isHeaderWritten:"+isHeaderWritten);
f = new FileOutputStream(fileName, false);
fileHeader = "requestStatus,cNumber,pId,id,token";
}
p = new PrintStream(f);
this.interpreter.setOut(p);
if(isHeaderWritten == "false"){
print(fileHeader);
JMeterUtils.setProperty("isHeaderWritten", "true");
}
log.info("Test results will be written to file "+fileName);
cNumber=vars.get("cNumber");
log.info("cNumber"+cNumber);
pId = vars.get("pId");
log.info("pId"+pId);
pmId = vars.get("pmId");
log.info("pmId"+pmId);
tTxt = vars.get("tTxt");
log.info("tTxt"+tTxt);
responseCode=prev.getResponseCode();
log.info("responseCode of "+requestString+ " is "+responseCode);
requestStatus = "Success";
if(!"201".equals(responseCode)){
requestStatus = "Failure"+","+cNumber+","+pId+","+pmId+","+tTxt;
}
result = requestStatus;
if("Success".equals(requestStatus)){
responseData=new String(data);
log.info("responseData of "+requestString+ " is "+responseData);
requestString=requestStatus+","+cNumber+","+pId+","+pmId+","+tTxt;
result = requestString;
log.info("result"+result);
}
log.debug(result);
print(result);
f.close();
According to JMeter Documentation:
Properties are global to JMeter, so can be used to communicate between threads and thread groups
Properties are not the same as variables. Variables are local to a thread; properties are common to all threads
So once you execute this line:
JMeterUtils.setProperty("isHeaderWritten", "true");
the property isHeaderWritten will become true and will remain true until you restart JMeter.
The easiest solution would be adding a tearDown Thread Group to your test plan (this Thread Group is being executed after any other thread group(s)) and add a JSR223 Sampler with the following code:
SampleResult.setIgnore();
org.apache.jmeter.util.JMeterUtils.setProperty("isHeaderWritten", "false");
Since JMeter 3.1 users are encouraged to switch to JSR223 Test Elements and Groovy language for any form of scripting mainly because Groovy performance is much better than Beanshell so consider migrating to JSR223 PostProcessor and Groovy language on next available opportunity

Jmeter: How to extract assertion results and use it for my API's

My test plan looks like,
I am running some Http requests with assertion .
I need to check assertion is passed or failed (any built-in variable is available?) .
Need to call/extract assertion result and use it for my next API .
Any one help me on this.
I solved this case using BeanShell Listener by adding the following code and you can call assertion result on jmeter like ${results}
try
{
int results;
if (sampleResult.isSuccessful()) {
log.info("Response Assertion PASSED");
results = 1;
vars.put("results","1");}
else {
log.info("Response Assertion FAILED" );
results = 2;
vars.put("results","2");}
}
catch (Throwable ex) {
log.error("Error in Beanshell", ex);
throw ex;
}
I would suggest adding JSR223 Assertion where you can get parent sampler AssertionResult instance(s) and extract the required information from it like:
def assertionResult = SampleResult.getAssertionResults()[0]
if (assertionResult.isFailure()) {
log.info("Assertion " + assertionResult.getName() + " failed")
log.info(assertionResult.getFailureMessage())
}
Demo:
See How to Use JMeter Assertions in Three Easy Steps article for more information on using assertions. Also avoid using Beanshell as it may cause performance problems, if you need to go for scripting - stick to JSR223 Test Elements
1.Use "Regular Expression Extractor" to extract assertion result.
2.Use BeanShell Sampler to get assertion result,then,use it for your next API.

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

How to clear header manager programmatically

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

Resources