How to validate json response in bean shell and perform some action in case the response is not as expected in jmeter? - jmeter

I want to extract the json response in jmeter and if the response is not as expected, i need to print it to a csv file.
i tried using the contains method to check if the response contains an expected keyword but it doesn't seem to work. Is there anyother way I can do this?
Sample Code here:
log.info(ctx.getPreviousResult().getResponseDataAsString());
r = ctx.getPreviousResult().getResponseCode();
d = ctx.getPreviousResult().getResponseDataAsString();
if(!d.contains("valid")){
p.println(r +","+ vars.get("email") +",");
}
This is my json response
{
"isBlueLinkServicePinValid": "valid"
}
I'm checking for the keyword "valid"
if(!d.contains("valid"))
But it doesn't seem to work?
TIA

Since JMeter 3.1 it is not recommended to use Beanshell for scripting, you should go for JSR223 Test Elements and Groovy language instead. The main reason is that Groovy has much better performance than Beanshell does.
Groovy has built-in JSON support therefore you can extract isBlueLinkServicePinValid attribute value in an easy way:
String response = prev.getResponseDataAsString();
log.info("Response: " + response)
String valid = new groovy.json.JsonSlurper().parseText(response).isBlueLinkServicePinValid
log.info("Valid: " + valid);
if (valid.equals("valid")) {
log.info("Do something");
}
else {
log.info("Do something else");
}
Demo:

Related

SpringBoot WebClient - exchangeToMono

I need to make a external API call from my Spring service. I am planning to return a Response object,
containing status code, actual json response. So that, the caller can decide how to decode the json based on the status code. Example - If status is 2xx - save the response, 4xx - log it in some error table etc.
I have done something like this. But, I am not sure whether the approach is correct or not.
Basically, I need to return the json and the status code for all status codes.
Wondering, how to achieve this with exchangeToMono.
The below code snippet works fine but I am not sure it's correct or not.
Appreciate any help or suggestions...
ResponseObj has two properties - statusCode and json.
public ResponseObj getExternalData() {
ResponseObj obj = new ResponseObj();
Mono<String> result = webclient.get().
.uri("/some/external/api")
.headers( //headers)
.exchangeToMono( response -> {
obj.setStatusCode(response.rawStatusCode());
return response.bodyToMono(String.class);
});
obj.setJson(result.block));
return obj;
}

Trim and split JSON response body using JMeter

I am trying to test REST APIs using JMeter. My lead told me to do load testing on each API using JMeter. Currently I am testing a Get API request, and I am getting the below JSON response body.
"https://api.rewards.com:/lists/v1/listcontainer/1?ts=20190221004021&auth=EngineeringSolutions:ydvMMlY2uxiKG0yuwh1IbVgR2mfqTQaQncTEaMr+Ef0="
Now I have to pass this JSON body to another HTTP request and test the API.
My questions:
How can I trim double quotation characters from the JSON response body?
How can I get the values of ts and auth using split method like (ts=20190221004021 and auth=EngineeringSolutions:ydvMMlY2uxiKG0yuwh1IbVgR2mfqTQaQncTEaMr+Ef0=)
I know I can use a Regular expression Extractor or BeanShell PreProcessor to do all the actions, but I don't know how to do it. Can anyone guide me how I can trim and split the JSON response?
You don't trim off doublequotes, they are part of JSON syntax.
There are many methods to split the query string in URL, here are 2 of them.
var url = "https://api.rewards.com:/lists/v1/listcontainer/1?ts=20190221004021&auth=EngineeringSolutions:ydvMMlY2uxiKG0yuwh1IbVgR2mfqTQaQncTEaMr+Ef0=";
//method 1: pure js
var queries = url.match(/(.+)\?(.+)/)[2].split("&");
var params = {};
for (let i=0; i<queries.length; i++){
var item = queries[i].match(/([0-9A-Za-z]+)=(.+)/);
params[item[1]] = item[2]
}
console.log(JSON.stringify(params,null,2));
//method 2: new in ES6
var queryStr = url.match(/(.+)\?(.+)/)[2];
var urlParams = new URLSearchParams(queryStr);
var params = {};
for (let item of urlParams.entries())
params[item[0]] = item[1];
console.log(JSON.stringify(params,null,2));

jmeter - how to make a groovy script easier to maintain for extentreports

Below is a script that helps me build an extentreport for jmeter. It is a JSR223 PostProcessor element. It's working nicely however, the problem is that I have it duplicated after every HTTP Request in the script. I have several scripts with 100's of HTTP requests that would need essentially a copy of the same PostProcessor groovy script. This = hard to maintain!
I have tried splitting common parts into an external groovy script that I tried calling on the JSR223 PostProcessor. I also tried chunking up the bits of the script and putting the values into a csv so that I could just update the csv values if anything changed.
I'm sure there's a cleaner/better way to do this but I'm still learning so I'm not sure of the best way to make this easier to maintain. Here's the JSR223 PostProcessor. The only bit that changes with each http request is the "//test result" section
import com.relevantcodes.extentreports.ExtentReports;
import com.relevantcodes.extentreports.ExtentTest;
import com.relevantcodes.extentreports.LogStatus;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
//configure object for response data
def response = prev.getResponseDataAsString();
//configure extentreports objects
ExtentReports report;
ExtentTest testLogger;
//set location for file and report config
String resultsPath = "C:/portalQA/Automation/Results/";
String configPath = "C:/portalQA/Automation/JMeter/TestReportConfig/";
String reportPath =
resultsPath+"Login_Results_${reportDate}_${currentTime}_${testenv}.html";
File file = new File(reportPath);
if (!file.exists()) {
//if file does not exist, create it
report = new ExtentReports(reportPath, true);
report.loadConfig( new File(configPath+"extent-config.xml"));
} else {
//else append to existing report
report = new ExtentReports(reportPath, false);
report.loadConfig( new File(configPath+"extent-config.xml"));
}
//test result
testLogger = report.startTest("Authenticate");
testLogger.assignCategory("Initialize Session");
if (response.contains("com.blah.portal.model.User")) {
testLogger.log(LogStatus.PASS, "Logged in with: ${username}");
testLogger.log(LogStatus.INFO, response);
} else {
testLogger.log(LogStatus.FAIL, "Could not authenticate session");
testLogger.log(LogStatus.INFO, response);
}
log.info("Authenticate");
print("Authenticate print");
report.endTest(testLogger);
report.flush();
I see two options:
I suggest using JSR223 Listener instead. First of all, that way you will only have 1 listener in your script, which resolves your original problem, but it is a better option for writing into file in general, since listener has only one instance for all running threads, so you won't be creating a race condition when writing to file.
If you rather have a post-processor, you can put it on higher level (not under any particular sampler) which will cause it to run after each request within the same scope or below.
For example, configuration like
Thread Group
Post-processor
Sampler 1
...
Sampler N
Will cause Post-processor to run after each Sampler 1...Sampler N
In both cases you may need to check which sampler you are processing, and skip those you don't want to add to your report (easiest way to do it, is to come up with some name convention for excluded samplers)
I also faced the same challenge. In my case I need to check if JSON response from REST service was correct. I solved it in the following way.
I've created a JSR223 PreProcessor under the script root. It contains my custom class to handle JSON parsing and asserts.
import groovy.json.JsonSlurper
import org.apache.jmeter.assertions.AssertionResult
class CustomAssert {
def parseResponse(json) {
def jsonSlurper = new JsonSlurper()
return jsonSlurper.parseText(json)
}
def assertResult(assertionResult, expectedResult, actualResult) {
if (!expectedResult.equals(actualResult)) {
assertionResult.setFailure(true);
assertionResult.setFailureMessage("Expected ${expectedResult} but was ${actualResult}");
}
}
}
vars.putObject('customAssert', new CustomAssert())
Note the last line:
vars.putObject('customAssert', new CustomAssert())
I put an instance of my CustomAssert to vars.
Then under my HTTP Requests I've added JSR233 Assertion
def a = vars.getObject('customAssert')
def response = a.parseResponse(prev.getResponseDataAsString())
a.assertResult(AssertionResult, 'DRY', response.sensorResultHolderUIs[0].result.toString())
a.assertResult(AssertionResult, 'DRY', response.sensorResultHolderUIs[1].result.toString())
a.assertResult(AssertionResult, 'DRY', response.sensorResultHolderUIs[2].result.toString())
It basically retrieves the instance of CustomAssert from vars and calls its methods. I can put as many JSR233 Assertions as I want. The only code that is copied is those two lines on top:
def a = vars.getObject('customAssert')
def response = a.parseResponse(prev.getResponseDataAsString())
To sum up:
Take the common part of your code (that doesn't have to be copied).
Wrap it in a class.
Put the class in JSR233 PreProcessor under the root and export its instance via vars
Take the rest of your code and adjust it to use class defined in 2.
Put that code in as many JSR233 Assertions as you want remembering to retrieve the instance created in 3. from vars
Thank you user1053510. Your advice lead me to build my own JSR223 Listener that renders the report. Below is the code in my JSR223 Listener:
import com.aventstack.extentreports.*;
import com.aventstack.extentreports.reporter.*;
import com.aventstack.extentreports.markuputils.*;
ExtentHtmlReporter htmlReporter;
ExtentReports extent;
ExtentTest test;
// create the HtmlReporter
htmlReporter = new ExtentHtmlReporter("C:/AUTO_Results/Results_${testApp}_${reportDate}_${currentTime}_${testenv}.html");
//configure report
htmlReporter.config().setCreateOfflineReport(true);
htmlReporter.config().setChartVisibilityOnOpen(true);
htmlReporter.config().setDocumentTitle("${testApp} Results");
htmlReporter.config().setEncoding("utf-8");
htmlReporter.config().setReportName("${testApp} Results ${reportDate}_${currentTime}_${testenv}");
htmlReporter.setAppendExisting(true);
// create ExtentReports
extent = new ExtentReports();
// attach reporter to ExtentReports
extent.attachReporter(htmlReporter);
extent.setReportUsesManualConfiguration(true);
// Show Env section and set data on dashboard
extent.setSystemInfo("Tool","JMeter");
extent.setSystemInfo("Test Env","${testenv}");
extent.setSystemInfo("Test Date","${reportDate}");
extent.setSystemInfo("Test Time","${currentTime}");
//stringify test info
String threadName = sampler.getThreadName();
String samplerName = sampler.getName();
String requestData = props.get("propRequestData");
String respCode = props.get("propRespCode");
String respMessage = props.get("propRespMessage");
String responseData = props.get("propResponse");
// create test
test = extent.createTest(threadName+" - "+samplerName);
//test.assignCategory("API Testing");
// analyze sampler result
if (vars.get("JMeterThread.last_sample_ok") == "false") {
log.error("FAILED: "+samplerName);
print("FAILED: "+samplerName);
test.fail(MarkupHelper.createLabel("FAILED: "+sampler.getName(),ExtentColor.RED));
} else if (vars.get("JMeterThread.last_sample_ok") == "true") {
if(responseData.contains("#error")) {
log.info("FAILED: "+sampler.getName());
print("FAILED: "+sampler.getName());
test.fail(MarkupHelper.createLabel("FAILED: "+sampler.getName(),ExtentColor.RED));
} else if (responseData.contains("{")) {
log.info("Passed: "+sampler.getName());
print("Passed: "+sampler.getName());
test.pass(MarkupHelper.createLabel("Passed: "+sampler.getName(),ExtentColor.GREEN));
}
} else {
log.error("Something is really wonky");
print("Something is really wonky");
test.fatal("Something is really wonky");
}
//info messages
test.info("RequestData: "+requestData);
test.info("Response Code and Message: "+respCode+" "+respMessage);
test.info("ResponseData: "+responseData);
//playing around
//markupify json into code blocks
//Markup m = MarkupHelper.createCodeBlock(requestData);
//test.info(MarkupHelper.createModal("Modal text"));
//Markup mCard = MarkupHelper.createCard(requestData, ExtentColor.CYAN);
// test.info("Request "+m);
// test.info(mCard);
// test.info("Response Data: "+MarkupHelper.createCodeBlock(props.get("propResponse")));
// test.info("ASSERTION MESSAGE: "+props.get("propAssertion"));
// end the reporting and save the file
extent.flush();
Then in each threadgroup I have a BeanShell Assertion with these lines:
//request data
String requestData = new String(prev.SamplerData);
//String requestData = new String(requestData);
props.put("propRequestData", requestData);
//response data
String respData = new String(prev.ResponseData);
//String respData = new String(prev.getResponseDataAsString());
props.put("propResponse", respData);
//response code
String respCode = new String(prev.ResponseCode);
props.put("propRespCode",respCode);
//response message
String respMessage = new String(prev.ResponseMessage);
props.put("propRespMessage",respMessage);

Concatenating responses in jmeter

I tried a lot in google and did not find any solution. If I missed then I am sorry.
In Jmeter, I am running the same request in a loop. For 'n' number of times. For every request, I need to extract the json response and pass it to next request. I am able to extract the response of last one request and save it to a variable and then pass to next request. I used JSON Path Extractor. I also figured out extracting the response using BeanShell and JSR223 Pre and Post Processors
The thing here is I need to extract all previous responses data and build the request body for the next request, not just last 1 response. Please
I do not want to append the extracted response to a file then pass the data to request from the file.
Request1 (Requestbody:[]). Response1: Product A
Request2 (Requestbody: [Product A]). Response: Product B
Request3 (Requestbody: [Product A, Product B]. Response Product C
Request4 (Requestbody: [Product A, Product B, Product C]). Response: Product
.. ... .....
Requestn (body: [Product A, Product B, Product C, Product D]....), response: no more products
Any thoughts please
Thanks Jack
If you need to build a JSON Array from previous responses I would recommend consider using JSR223 PostProcessor (assumes Groovy language) and JSONBuilder class for this.
Groovy has built-in JSON support therefore you will have the full flexibility in reading and writing arbitrary JSON structures.
Example:
References:
Parsing and producing JSON
Creating JSON using Groovy
Groovy Is the New Black
hmm.. what a requirement. I wonder what would be the use case. :) I achieved this with a LinkedList and passing the object back and forth between the pre and post processors using getObject and putObject. The below codes contains plenty of debug statements. Please discard them. YOu can also optimize the same.
HTTP Sampler
with "BODY DATA" tab having just ${request}
--> Beanshell pre-processor
log.info("Entering preprocessor..");
LinkedList itemsArrayLocal = vars.getObject("itemsArrayLocal");
if ( itemsArrayLocal == null) {
try {
itemsArrayLocal = new LinkedList();
//itemsArrayLocal.add("");
vars.putObject("itemsArrayLocal", itemsArrayLocal );
vars.put("request", "(Requestbody:[" + "" + "]");
}
catch (Exception e) {
e.printStackTrace();
log.info(e);
}
}
else {
String s = "";
for ( int i=0; i < itemsArrayLocal.size(); i++) {
if ( i >= 1) {
s = s + ",";
}
s = s + itemsArrayLocal.get(i).toString() ;
log.info("i=" + String.valueOf(i) + s);
}
log.info("s=" + s);
vars.put("request", "(Requestbody:[" + s + "]");
}
--> Beanshell post-processor
log.info("Entering POST PROCESSOR..");
LinkedList itemsArrayLocal = (LinkedList) vars.getObject("itemsArrayLocal");
String o = prev.getResponseDataAsString().substring(2,10);
//log.info("o=" + o);
try {
log.info("Added..");
itemsArrayLocal.add(o);
log.info("Size=" + String.valueOf(itemsArrayLocal.size()));
}
catch (Exception e) {
e.printStackTrace();
log.info(e);
}

How to config a HTTP Request Method GET with JSON BODY in JMeter?

I'm facing the issue while writing the scenario in JMeter. It's API using GET Method and requires JSON BODY.
It's very easy if the method is POST/PUT. But I don't know how to do with method GET. I tried: Add HTTP Header Manager with Content-Type:application/json, but nothing help.
As I know, using BODY with GET request is the not the good way, but the developer team has implemented like that, and it can be worked with curl.
So I would like to know can we config this in JMeter or not? and How?
Thanks in advance.
In fact sending request body with HTTP GET request is not supported by Apache HttpComponents and hence in JMeter, you should be able to send a GET request with JSON body using JSR223 Sampler and the following code (assumes Groovy language):
import org.apache.http.HttpResponse
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClientBuilder
import org.apache.http.util.EntityUtils
public class HttpGetWithBody extends HttpEntityEnclosingRequestBase {
public final static String METHOD_NAME = "GET";
#Override
public String getMethod() {
return METHOD_NAME;
}
}
def client = HttpClientBuilder.create().build();
def getRequest = new HttpGetWithBody();
getRequest.setURI(new URL("http://example.com").toURI());
def json = "{\"employees\":[\n" +
" {\"firstName\":\"John\", \"lastName\":\"Doe\"},\n" +
" {\"firstName\":\"Anna\", \"lastName\":\"Smith\"},\n" +
" {\"firstName\":\"Peter\", \"lastName\":\"Jones\"}\n" +
"]}";
def body = new StringEntity(json, "application/json", "UTF-8");
getRequest.addHeader("Content-Type", "application/json");
getRequest.setEntity(body);
def response = client.execute(getRequest);
def result = EntityUtils.toString(response.getEntity());
log.info(result);
See Beanshell vs JSR223 vs Java JMeter Scripting: The Performance-Off You've Been Waiting For! article for more information on using JSR223 test elements and groovy language and scripting best practices.
There is a reported bug in JMeter for this https://bz.apache.org/bugzilla/show_bug.cgi?id=60358 and looks like the fix is on the way. Hopefully this issue would be fixed soon.

Resources