I don't know hardly anything about XML. I have successfully gotten a SOAP response (using things like SOAPUi and Boomerang) from an asmx web service. It's a large file.
Now I need to get it to regular delimited columns. Is there a simple way to do this?
My file is attached here
Not sure if it is required one time transformation or do this job quite frequently.
So, adding the answer here with some more details.
Approach #1: Using on-line
As mentioned in the comments, you can use the on-line site to convert your xml data into csv.
Even it requires to do some pre-process with the message / response that you have i.e.,
save the data into file
remove headers or unwanted data etc or make it ready to be usable in the above mentioned online site.
The disadvantages in this approaches
requires some manual work
expose data on public, but at times may be possible to share
time taking
can not use it an automated fashion
difficult to repeat
Approach #2: Using Groovy Script
So, this approach addresses the disadvantages of #1 approach.
Here is the Groovy Script which reads previous soap request step's response, and gives the data into a csv file.
In your test case, add a new groovy script test step right after the soap request step which gives you the data and copy below script content into it. i.e., (Test Case -> Step 1: Soap Request where you are getting responseStep 2: Groovy Script (with below script))
Add a test case custom property, say OUTPUT_FILE_NAME and provide the file path for csv to be saved at. Even, if you do not provide this property, it will automatically saves the csv file chargedata.csv under System temp directory.
You may find the comments in-line
/**
* this script will read the previous step response
* extract the cdata at the given xpath
* read all the records and transfroms into csv file
**/
import com.eviware.soapui.support.XmlHolder
import groovy.xml.*
/**Define the output file name in test case custom property say OUTPUT_FILE_NAME and value as absolute file path
* otherwise, it write a file chargedata.csv in system temp directory
**/
def outputFileName = context.testCase.getPropertyValue('OUTPUT_FILE_NAME') ?: System.getProperty("java.io.tmpdir")+ '/chargedata.csv'
//csv field separator - change it if needed
def delimiter = ','
/**
* Below statement will fetch the previous request step response.
*/
def response = context.testCase.testStepList[context.currentStepIndex - 1].testRequest.response.responseContent
//Create the xml holder object to get the xpath value which cdata in this case
def responseHolder = new XmlHolder(response)
def xpath = '//*:Charges_FileResponse/*:Charges_FileResult'
//Get the cdata part from above xpath which is a string
def data = responseHolder.getNodeValue(xpath)
//This again parses the xml inside of cdata
def chargeRecords = new XmlParser().parseText(data)
//This is going hold all the data from ChargeRecords
def chargeRecordsDataStructure = []
//This is to hold all the headers
def headers = [] as Set
/**
* This is to create Charge data
**/
def buildChargeDataStructure = { charge ->
def chargeDataStructure = new Expando()
charge.children().each {
def elementName = it.name()
def elementText = it.value().join()
chargeDataStructure[elementName] = elementText
//Add to field name to the list if not already added
(elementName in headers) ?: headers << elementName
}
chargeDataStructure
}
/**
* this is to create a csv row in string format
**/
def createRow = { recordDataStructure ->
def row = new StringBuffer()
headers.each {
if (row) {
row += delimiter + recordDataStructure[it] ?: ''
} else {
row += recordDataStructure[it] ?: ''
}
}
row.toString()+'\n'
}
//Build the whole data structure of Charge Records
chargeRecords.Charge.each { charge ->
chargeRecordsDataStructure << buildChargeDataStructure( charge )
}
//Build the rows
def rows = new StringBuffer()
rows << headers.join(',') +'\n'
chargeRecordsDataStructure.each { rows << createRow (it)}
//Write the rows into file
new File(outputFileName).text = rows
Related
I have getuserdetails api where I need to send purchased item in array
API: api/getuserdetails/${id}
Method: post
body: {"uname":{
"purchaseitem": ["121","11","4","12345"]
}
}
In jmeter setup looks like
>>TP_1
>>CSVDataSet_ids
>>CSVDataSet_purchaseitem
>> ThreadGroup1
>>HTTP Req
>>JSR223 PreProcessor
>>View result tree
purchaseitem csv has values like
1231
12121
312232
13
1
42435
133
I want to pass "purchaseitem" array values fetched from CSV in comma separated manner as shown in body
I've tried something like this in JSR223 PreProcessor
def list = ${purchaseitem}
for (i=0;i<=list.size()-1;i=i+10)
{
def mylist = list.subList(i,i+10);
props.put ("mylist"+i,mylist)
}
Can someone please help? Or is there any function to solve this simple problem?
You're doing something weird
You won't be able to use CSV Data Set Config because it reads the next value with next virtual user/iteration
Your code doesn't make sense at all
If you need to send all the values from the CSV file at once you should be rather using JsonBuilder class, example code which generates JSON and prints it to jmeter.log file:
def payload = [:]
def purchasedtem = [:]
purchasedtem.put('purchaseitem', new File('/path/to/your/purchase.csv').readLines().collect())
payload.put('uname', purchasedtem)
log.info(new groovy.json.JsonBuilder(payload).toPrettyString())
More information:
Apache Groovy - Parsing and Producing JSON
Apache Groovy - Why and How You Should Use It
I want to save soap test step raw request & step in a path which is read from configuration file imported in test suite custom properties.
How can I do that?
Using the below script but with fixed location that was defined in the script.
def myOutFile = "D:/TestLog/Online_Test/PostPaidSuccess_Payment_BillInqReq.xml"
def response = context.expand( '${BillInq#Request}' )
def f = new File(myOutFile)
f.write(response, "UTF-8")
I would suggest to avoid additional Groovy Script test step to just store previous step request / response.
Below script assumes that, there is user defined property (REQUEST_PATH) at test suite level with its value(valid file path to store data, path separated by forward slash '/' even on windows).
Instead use Script Assertion for the Billing request step itself(one more step less in the test case)
//Stores raw request to given location using utf-8 encoding
new File(context.testCase.testSuite.getPropertyValue('REQUEST_PATH') as String).write(context.rawRequest,'utf-8')
Actually there is a small difference between context.request and context.rawRequest and the above script using rawRequest.
context.request - will have the variables as it is, not the actual value.
For eg:
<element>${java.util.UUID.randomUUID().toString()}</element>
Where as context.rawRequest - will have the actual value that was sent in the request.
For eg:
<element>4ee36185-9bfb-47d2-883e-65bf6d3d616b</element>
EDIT Based on comments: Please try this for ACCESS DENIED issue
def file = new File(context.testCase.testSuite.getPropertyValue('REQUEST_PATH') as String)
if (!file.canWrite()) {
file.writable = true
}
file.write(context.rawRequest,'utf-8')
EDIT2 Based on further comments from OP, the request file name should be the current test step name.
//Create filename by concatenating path from suite property and current test stepname
def filename = "${context.testCase.testSuite.getPropertyValue('REQUEST_PATH')}/${context.currentStep.name}.xml" as String
new File(filename).write(context.rawRequest,'utf-8')
in soapui my project is :
Project
|__Datasource
|__request
|__groovy_code
|__DatasourceLoop
My Datasource contains 100 lines, each one is a request with different parameters.
My groovy_code save the id from the response of the request.
When i run my project, it executes 100 requests without errors.
groovy_code save only the first id.
What i want is to save id for each request, so 100 ids in different variables at project level
Here is my groovy_code:
import groovy.json.JsonSlurper
def response = context.expand( '${login#Response#declare namespace ns1=\'https://elsian/ns/20110518\'; //ns1:login_resp[1]/ns1:item[1]/ns1:response[1]}' )
def slurper = new JsonSlurper()
def result = slurper.parseText(response)
log.info result.data.id
testRunner.testCase.testSuite.project.setPropertyValue("token_id", result.data.id)
Thank you for your help
I never use SOAPUI PRO and I don't have access to datasource testStep or even datasource loop.
However based on the project structure you're showing I suppose that for each time datasource loop founds a element in datasource it sends the flow to request step so request and groovy steps are executed on each iteration; due to this I think that the problem is that your groovy code is overriding each time the same property with a new value.
Then to solve this you can try adding some variable suffix to your property name to avoid override each time the property value. For example you can add to token_id string a counter, some uuid, current ms etc.
For example you can use a counter as a suffix. To keep the counter value you've to save it in the context variable, this way this property are shared between your tests inside the current execution:
import groovy.json.JsonSlurper
// create a suffix function to generate
// the suffixs for your property names based on a count
def getSuffixNameProperty = {
// check if already exists
if(context['count']){
// if exists simply add 1
context['count']++
}else{
// if not exists initialize the counter
context['count'] = 1
}
return context['count']
}
def propertyName = "token_id" + getSuffixNameProperty();
def response = context.expand( '${login#Response#declare namespace ns1=\'https://elsian/ns/20110518\'; //ns1:login_resp[1]/ns1:item[1]/ns1:response[1]}' )
def slurper = new JsonSlurper()
def result = slurper.parseText(response)
testRunner.testCase.testSuite.project.setPropertyValue(propertyName, result.data.id)
I've this code by which I am trying to set the value of a Key (a json node id). But its not setting the value. Log.info statement is showing right values.
Key= context.expand( '${#Project#key}' )
Value= context.expand( '${#Project#value}' )
Binding binding = new Binding()
binding.setVariable("v", "$Value")
binding.setVariable("k", "$Key")
log.info(binding.getVariable("v")) // gives me the value 1234
log.info(binding.getVariable("k")) // gives me the value request.id
def SetKey = new GroovyShell(binding).evaluate( "k=v")
Can someone please comment on whats wrong in this code. and how can I correct it.
Edit: Explanation of the issue
In SoapUI I've some json nodes saved in data source like this request.id and request.app.id and there expected values in Value column which I am fetching through Key and Value above. I am hoping to change the value of a json node to its respective value at run time. So for each iteration of data source, it should set the correct value of that particular json node. Befor the above code I've parsed my json request by json slurper and after the above code I am building the json again by Json builder and running the request. Parsing and running the request works fine, its just that I couldnt set the value of the json node.
If your keys are just dotted names, you could simply use some string manipulation, no need to involve the Groovy parser.
For example, if they all begin with request:
def steps = Key.split(/\./)
if (steps.size() < 2 || steps[0] != 'request') {
throw ...
}
def obj = request
if (steps.size() > 2) {
steps[1..-2].each {
obj = obj[it]
}
}
obj[steps[-1]] = Value
I am using SoapUI to test webservices. The following string (in xml format) is my request:
<Request>
<AC>
<AssetID>1</AssetID>
<Asset_Name>ABC</Asset_Name>
<Asset_Number>1</Asset_Number>
</AC>
<AC>
<AssetID>2</AssetID>
<Asset_Name>XYZ</Asset_Name>
<Asset_Number>2</Asset_Number>
</Ac>
</Request>
I am using the following code in a groovy script to extract value of Asset_Number for each AC (The above xml string is stored in variable strRequest):
def x = new XmlSlurper().parseText("$strRequest")
x.AC.each { AC ->
assetNum = AC."Asset_Number"
<<do something with the assetNum>>
}
However, I wish to parameterize the above code to pick up Asset_Number for various types of assets (e.g. AC, Peripheral etc). The request xml for each asset is in the same format as above. If I replace 'AC' with variable name 'requestName' in above code:
//strRequest = xml request
def requestName //I will pick up value for this from a test case property
def x = new XmlSlurper().parseText("$strRequest")
x.(requestName.toString()).each { requestName ->
assetNum = requestName."Asset_Number"
<<do something with the assetNum>>
}
it shows the following error:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script166.groovy: 35: The current scope already contains a variable of the name requestName # line 35, column 2. { ^ org.codehaus.groovy.syntax.SyntaxException: The current scope already contains a variable of the name requestName
I have tried a solution mentioned in another post Using a String as code with Groovy XML Parser, but it doesn't serve my purpose.
Any other ideas?
You can use
x."$requestName".each
Why XmlSlurper? In SoapUI you can use XmlHolder
import com.eviware.soapui.support.XmlHolder
def responseHolder = new XmlHolder(messageExchange.getResponseContentAsXml())
def resultFromResponse = responseHolder["here_is_your_xpath"]
assert someExpectedResult == resultFromResponse
And if you need to iterate via multiple xml nodes
resultFromResponse.each
{
assert result == resultFromResponse
}