How to save id using groovy from response? - groovy

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)

Related

JMeter : Using the JSON Postprocessor _ALL variable in one more postprocessor to fetch the data

I'm having a JSON data, from which I'm able to fetch some data from JSON Extractor post-processor.
And the data looks something like this,
group_1 = ["Scooter"]
group_2 = ["Bus","Jeep","Car"]
group_ALL = ["Scooter"],["Bus","Jeep","Car"]
group_matchNr = 2
So I want to fetch all this data (or accumulate) in only one array variable,
Example : groups=["Scooter","Bus","Jeep","Car"]
so that I loop it through all the individual data in the api calls in a foreach loop.
example api call in foreach loop: https://vehicles.com/${groups}
My question is, I'm trying to write the groovy script to get all the data in groups variable. But unable to. I need your help in this and to configure the foreach controller also.
Groovy Script I tried in JSR223 Postprocessor which did not work:
import groovy.json.JsonSlurper;
groups= vars.get("group_ALL");
def jsonSlurper = new JsonSlurper();
jsonData = JSON.stringify(vars.get("groups"))
String tempRemoveBr = jsonData.replaceAll("[","");
String tempRemoveBr1 = tempRemoveBr.replaceAll("]","");
String[] arrVehicles = tempRemoveBr1.split(",")
for (int i=0;i<arrVehicles.size();i++)
{
log.info("arrVehicles[i]")
}
vars.put("arrVehicles",arrVehicles)
foreach controller configured as :
Input variable : arrVehicles
start index : 0
output variable name : vehicleN
Most probably you could achieve it using JSON JMESPath Extractor, however I cannot come up with a proper configuration without seeing your full response.
If you want to process existing variables here is a Groovy script you could use:
def group1 = new groovy.json.JsonSlurper().parseText(vars.get('group_1'))
def group2 = new groovy.json.JsonSlurper().parseText(vars.get('group_2'))
def arrVehicles = group1 + group2
vars.put('arrVehicles', new groovy.json.JsonBuilder(arrVehicles).toString())
More information:
Apache Groovy - Parsing and Producing JSON
Apache Groovy: What Is Groovy Used For?

How to convert soap xml response to delimited

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

How can we add SOAP request Test step in a Test Case using groovy scripts

I am looking for adding a SOAP request Test step in a test case, from a different TestSuite and test case, i have already coded the part to add Groovy script for same requirement but not able to add a SOAP request test step. Any help?
following is my code:
import com.eviware.soapui.impl.wsdl.teststeps.registry.GroovyScriptStepFactory
suite = context.testCase.testSuite.project.addNewTestSuite("AutomatedTestSuite")
tc = suite.addNewTestCase("automatedTestCase")
gs = tc.addTestStep( GroovyScriptStepFactory.GROOVY_TYPE, "GroovyScript1" )
gs2 = tc.addTestStep( GroovyScriptStepFactory.GROOVY_TYPE, "GroovyScript3" )
gs.properties["script"].value = 'log.info(\'hello world\')'
You can get the another testSuite,testCase and testStep by it's name through project as follows:
def project = context.testCase.testSuite.project
def testStep = project.testSuites['TestSuiteName'].testCases['TestCaseName'].testSteps['testStepName']
Or alternatively instead of array approach using getXXXXbyName method:
def testStep = project.getTestSuiteByName('TestSuiteName').getTestCaseByName('TestCaseName').getTestStepByName('testStepName')
Then to add this testStep to your testCase you can use cloneStep(WsdlTestStep testStep, String name) method.
All together in your script:
def suite = context.testCase.testSuite.project.addNewTestSuite("AutomatedTestSuite")
def tc = suite.addNewTestCase("automatedTestCase")
// get desired testStep
def project = context.testCase.testSuite.project
def testStep = project.testSuites['TestSuiteName'].testCases['TestCaseName'].testSteps['testStepName']
// add it to your new generated testCase
tc.cloneStep(testStep,testStep.name + "_Copy")
EDIT BASED ON COMMENT
If instead of a copy of another SOAP testStep you want to create a new one, you can do it using the follow code. Take in account that to create a testStep of SOAP type more info is needed than when you create a groovy one due to the need for wsdl operation info (in the example we take the first one but if you've more than one take care of what you take).
IMO the first approach is simpler, you can copy another testStep and change the properties you want... anyway if you want to do it this way here you're:
import com.eviware.soapui.impl.wsdl.teststeps.registry.WsdlTestRequestStepFactory
def suite = context.testCase.testSuite.project.addNewTestSuite("AutomatedTestSuite")
def tc = suite.addNewTestCase("automatedTestCase")
// get the WSDL operation... for the example we take the first one
// however if you've more get the correct one
def operation = testRunner.testCase.testSuite.project.getInterfaceAt(0).getOperationList()[0]
// factory to create the testStepConfig
def factory = new WsdlTestRequestStepFactory()
def config = factory.createConfig(operation,'stepName')
// create the testStep
def testStep = tc.addTestStep(config)
// change the request
testStep.properties['Request'].value = '<request>someData</request>'
Hope it helps,

SoapUI, temper request data break when value is read from property file

I have a groovy script as the first test step inside a test case, part of it looks like:
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def holder = groovyUtils.getXmlHolder("SampleTestt#Request").getXml()
log.info holder
When SampleTest test step has all element values hardcoded, the request xml can be printed fine.
However if some of the request values is read from a test case property, like the following for example
${#TestCase#Id}
The the above groovy script through error as:
org.apache.xmlbeans.XMLException: error: Unexpected character encountered : '$'
Can you please help?
Thanks.
You can use context.expand() to evaluate the properties inside your request and then parse the result to xmlHolder, your code could looks like:
// get your request replacing the properties inside by their values
def xmlRequest = context.expand('${SampleTestt#Request}')
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def holder = groovyUtils.getXmlHolder(xmlRequest)
log.info holder.getXml()
Note that I use SampleTestt as your test step request name, but I think that the last t could be a typo... check if it's the correct request name before use the code.
Hope this helps,

Setting json key using bind variables in groovy

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

Resources