I'm trying to build an HTTP request to a service which accepts a list of items, for example:
{
"user": "john",
"table": "goods",
"articles": [
{"id": "003", "quantity": 1},
{"id": "004", "quantity": 1},
{"id": "023", "quantity": 2},
{"id": "011", "quantity": 3},
{"id": "063", "quantity": 1},
{"id": "006", "quantity": 7}
]
}
My goal is to load ALL the articles from a CSV file, done as follows:
I would like to have something like:
{
"user": "john",
"table": "goods",
"articles": [
{"id": "${id}", "quantity": ${qte}}
]
}
Can anyone help me?
UPDATE:
Dmitri T solved my problem, thank you!
I also asked him how to add a JSON object (a dictionary) to my body, while he answer I found a solution which I will share here in case someone needs it. If you want to add:
"user": {"id": 1, "name": "John"}
to your body content, you just need to do this:
def user = [:]
//populate user
user.put('id', 1)
user.put('name', 'John')
//add user entity to body content
content.put("user", user)
Have fun!
You won't be able to have something like you provided above, the only way to dynamically build a JSON request body is constructing it programmatically using JSR223 PreProcessor
Add JSR223 PreProcessor as a child of the request which body you want to parameterize
Put the following code into "Script" area:
def content = [:]
content.put('user', 'john')
content.put('table', 'goods')
def articles = []
new File('test.csv').readLines().each { line ->
def article = [:]
article.put('id', line.split(',')[0])
article.put('quantity', line.split(',')[1])
articles.add(article)
}
content.put('articles', articles)
sampler.addNonEncodedArgument('', new groovy.json.JsonBuilder(content).toPrettyString(), '')
sampler.setPostBodyRaw(true)
That's it, when you run your test the PreProcessor will generate the request body from the CSV file and add it to the HTTP Request sampler on the fly.
References:
Apache Groovy: Parsing and producing JSON
Apache Groovy - Why and How You Should Use It
Related
There is "ForEach" controller in JMeter which takes an array of items and executes a sampler with each item. This is very useful when you need to execute a sampler with different parameters based on data received in previous request. However, "ForEach" controller runs samplers one after the other.
What I am looking for is, execute the samples in parallel. There is a plug in available in JMeter called, "bzm - Parallel Controller". However, this doesn't accept any input variable like "ForEach" controller does.
For example, I have following data in database. Author along with their books.
[
{
"firstName": "William",
"lastName":"Shakespeare",
"Title": "Mr",
"id": "1",
"books": [
{
"id": "WS1",
"title": "King John",
"year":"1596"
},
{
"id": "WS2",
"title": "Julius Caesar",
"year": "1599"
},
{
"id": "WS3",
"title": "Romeo and Juliet",
"year": "1595"
}
],
"Nationality": "English"
},
{
"firstName": "Sidney",
"lastName":"Sheldon",
"Title": "Mr",
"id": "2",
"books": [
{
"id": "SS1",
"title": "The Naked Face",
"year":"1969"
},
{
"id": "SS2",
"title": "A Stranger in the Mirror",
"year": "1976"
},
{
"id": "SS3",
"title": "Bloodline",
"year": "1977"
}
],
"Nationality": "American"
},
{
"firstName": "Eiichiro",
"lastName":"Oda",
"Title": "Mr",
"id": "3",
"books": [
{
"id": "EO1",
"title": "Wanted",
"year":"1992"
},
{
"id": "EO2",
"title": "Ikki Yako",
"year": "1993"
},
{
"id": "EO3",
"title": "Monsters",
"year": "1994"
}
],
"Nationality": "Japanese"
}
]
In my JMeter Test plan, I have defined a CSV Data Set Config file to store the ids of all authors in my system.
And then, there is a Thread Group. Inside thread group, I have a HTTP Sampler, GET /authors/{id}/books. for Example GET /authors/1/books. This will get all the books written by author "William Shakespeare".
Using JSON Extractor, I can capture the array of book ids returned by GET /authors/{id}/books.
There are 3 books with ids WS1, WS2 and WS3. Now for each of the books, I need to run another HTTP Sampler
PUT /books/WS1
PUT /books/WS2
PUT /books/WS3.
When I use ForEach controller, I can specify the input variable which was captured from previous JSON extractor. And it loops through each PUT request, for each book id. I want to do the same, but in PARALLEL, not sequential.
Does anyone know how to achieve this? or we have to write custom groovy/BeanShell script for this? If custom Groovy/BeanShell script is the only way, can you please tell me how to write this
Many thanks
You can do that by placing each test in a separate threadGroup.
You have a flag in the main test plan that controls if threads are executed in parallel or consecutively.
You're using not very correct test element, consider switching to Parallel Sampler instead.
Given you have the following JMeter Variables:
book_1=WS1
book_2=WS2
book_3=WS3
book_matchNr=3
You can add a Parallel Sampler and configure it to hit URLs containing ${book_1}, ${book_2} and ${book_3}
All the URLs to Retrieve will be executed in parallel:
More information: How to Use the Parallel Controller in JMeter
You could dynamically add URLs to Parallel HTTP Requests sampler.
Add JSR223 PreProcessor like a child to Parallel HTTP Requests sampler.
To script area add something like that:
String url = "https://examle.url.com/?book=";
1.upto(vars.get('book_matchNr') as int, {
index -> {
sampler.addURL(url + vars.get('book_' + index))
}
});
I am using get request for getting json response. And it is dynamic. My response looks like this.
{
"data": [
{
"id": 1,
"name": "Jack",
"gender": "male"
},
{
"id": 2,
"name": "Jill",
"gender": "female"
}
]
}
Can I use id from this response and put that id in 'for loop' to execute delete method?
Given path 'profile/delete/id'
And if I can then how?
Yes, refer the docs: https://github.com/intuit/karate#data-driven-features
* def result = call read('delete.feature') response.data
And in delete.feature you will be able to refer to the id variable directly.
The below request being the base request,
[
{
"name": "Test1",
"description": "testings",
"unitname": simple,
"ID": 02,
"val": "item"
},
{
"name": "Test2",
"description": "testing",
"unitname": simple3,
"ID": 23,
"val": "item"
}
]
I want to simulate this with multiple (1000) 'child' sections like the below in a single JMeter request:
It should create 1000 data set(name,description,unitname,ID,val) with unique values and then post the request. Instead of manually creating multiple tags, can i automate it or create a script to generate this automatically ?
[
{
"name": "Test1",
"description": "testings",
"unitname": simple,
"ID": 02,
"val": "item"
},
{
"name": "Test2",
"description": "testing",
"unitname": simple3,
"ID": 23,
"val": "item"
}
{
"name": "Test3",
"description": "testing",
"unitname": simple4,
"ID": 23,
"val": "item"
}
{
"name": "Test4",
"description": "testing",
"unitname": simple6,
"ID": 23,
"val": "item"
}
]
Any help please?
Add JSR223 PreProcessor as a child of your request where you need to send the generated JSON
Put the following code into "Script" area:
import groovy.json.JsonBuilder
import groovy.json.internal.LazyMap
import org.apache.commons.lang3.RandomStringUtils
def data = new ArrayList()
1.upto(1000, {
def entry = new LazyMap()
entry.put('name', 'test' + it)
entry.put('description', RandomStringUtils.randomAlphabetic(10))
entry.put('unitname', 'simple')
entry.put('ID', it)
entry.put('val', 'item')
data.add(entry)
})
def builder = new JsonBuilder()
builder(
data.collect {
[
name : it.get('name'),
descrtiption: it.get('description'),
unitname : it.get('unitname'),
ID : it.get('ID'),
val : it.get('val')
]
}
)
sampler.setPostBodyRaw(true)
sampler.addNonEncodedArgument("", builder.toPrettyString(), "")
Tick Cache compiled script if available box
Make sure groovy is selected in "Language" dropdown
That's it, the above script will generate a JSON Array and set it as the HTTP Request sampler's body.
More information:
Groovy: Parsing and producing JSON
Apache Groovy - Why and How You Should Use It
I am trying to extract first element from a json array. Below mentioned is json array
[
{
"cohortDefinition": {
"Key": 1151,
"id": 1798,
"srcId": "3526",
"pcKey": -1,
"userName": "CHROME_USER",
"name": "JMeter2017-01-06-1483749546167",
"Type": "SUBJECT",
"tool": "CB",
"count": 32757,
"extractionStatus": "",
"dateCreated": "2017-05-10T17:48:45Z"
},
"datasource": {
"id": 2,
"name": "health",
"subjectCount": 116352
},
"project": {
"id": 747,
"name": "Jmeter Project"
}
},
{
"cohortDefinition": {
"Key": 1150,
"id": 1796,
"srcId": "3525",
"pcKey": -1,
"userName": "CHROME_USER",
"name": "JMeter2016-10-27-1477620919644",
"Type": "SUBJECT",
"tool": "CB",
"count": 32757,
"extractionStatus": "",
"dateCreated": "2017-05-10T16:57:11Z"
},
"datasource": {
"id": 2,
"name": "health",
"subjectCount": 116352
},
"project": {
"id": 747,
"name": "Jmeter Project"
}
}
]
From above json i would like to extract first value ie. srcId": "3526".
I tried doing following expression in Jmeter extractor
$..cohortDefinition.srcId[1]
However it is not working. If anyone know how to do this please do let me know.
After JMeter 3.0, you can use JSON Extractor, see:
https://stackoverflow.com/a/47043204/460802
Before JMeter 3.0:
Please follow the below steps to retrieve srcId.
Add a JSON Path Extractor to your request and configure below values.
Destination Variable Name - myVar
JSON Path Expression - $..cohortDefinition.srcId - this will extract all the srcIDs from the JSON.
Default Value - Not Found or Err
Add a Debug Sampler and View Results Tree to your test plan.
Save it and execute.
In Debug Sampler, you can view all the srcId as shown below.
You can now use myVar_1 and myVar_2 in your test plan
using ${myVar_1} ${myVar_2}
No need for Plugin, JMeter has a JSON Extractor that will provide this feature:
Notice:
JSON Path Expression is: $..cohortDefinition.srcId
Match No : 1
I have a JSON file contact.txt that has been parsed into an object called JSONObj that is structured like this:
[
{
"firstName": "John",
"lastName": "Smith",
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021"
},
"phoneNumbers": [
{ "type": "home", "number": "212 555-1234" },
{ "type": "fax", "number": "646 555-4567" }
]
},
{
"firstName": "Mike",
"lastName": "Jackson",
"address": {
"streetAddress": "21 Barnes Street",
"city": "Abeokuta",
"state": "Ogun",
"postalCode": "10122"
},
"phoneNumbers": [
{ "type": "home", "number": "101 444-0123" },
{ "type": "fax", "number": "757 666-5678" }
]
}
]
I envision editing the file/object by taking in data from a form so as to add more contacts. How can I do this?
The following method for adding a new contact to the JSONObj's array doesn't seem to be working, what's the problem?:
var newContact = {
"firstName": "Jaseph",
"lastName": "Lamb",
"address": {
"streetAddress": "25 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "13021"
},
"phoneNumbers": [
{ "type": "home", "number": "312 545-1234" },
{ "type": "fax", "number": "626 554-4567" }
]
}
var z = contact.JSONObj.length;
contact.JSONObj.push(newContact);
It depends on what technology you're using. The basic process is to read the file in, convert it to whatever native datatypes (hash, dict, list, etc.) using a JSON parsing library, modify or add data to the native object, then convert it back to JSON and store it to the file.
In Python, using the simplejson library it would look like this:
import simplejson
jsonobj = simplejson.loads(open('contact.txt'))
#python's dict syntax looks almost like JSON
jsonobj.append({
'firstName': 'Steve',
'lastName': 'K.',
'address': {
'streetAddress': '123 Testing',
'city': 'Test',
'state': 'MI',
'postalCode': '12345'
},
'phoneNumbers': [
{ 'type': 'home', 'number': '248 555-1234' }
]
})
simplejson.dump(jsonobj, open('contact.txt', 'w'), indent=True)
The data in this example is hardcoded strings, but it could come from another file or a web application request / form data, etc. If you're doing this in a web app though I would advise against reading and writing to the same file (in case two requests come in at the same time).
Please provide more information if this doesn't answer your question.
In response to "isn't there way to do this using standard javascript?":
To parse a JSON string in Javascript you can either eval it (not safe) or use a JSON parser like this one's JSON.parse. Once you have the converted JSON object you can perform whatever modifications you want to it in standard JS. You can then use that same library to convert a JS object to a JSON string (JSON.stringify). Javascript does not allow file access (unless you're doing serverside JS), so that would prevent you from reading & writing to your contact.txt file directly. You'd have to use a serverside language (like Python, Java, etc.) to read and write the file.
Once you have read in the JSON, you just have an associative array - or rather you have a pseudo-associative array, since this is Javascript. Either way, you can treat the thing as one big list of dictionaries. You can access it by key and index.
So, to play with this object:
var firstPerson = JSONObj[0];
var secondPerson = JSONObj[1];
var name = firstPerson['firstName'] + ' ' + firstPerson['lastName'];
Since you will usually have more than two people, you probably just want to loop through each dictionary in your list and do something:
for(var person in jsonList) {
alert(person['address']);
}
If you want to edit the JSON and save it back to a file, then read it into memory, edit the list of dictionaries, and rewrite back to the file.
Your JSON library will have a function for turning JSON into a string, just as it turns a string into JSON.
p.s. I suggest you observe JavaScript conventions and use camelcase for your variable names, unless you have some other customs at your place of employment. http://javascript.crockford.com/code.html