Saving raw request & response of Soap test step in user defined path - groovy

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.x‌​ml"
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')

Related

Jmeter - groovy function not working as expected

I have a groovy function which performs some date operations. i.e. Returns a date 12 months forward. This date is encoded and stored into a variable.
snippet:
${__groovy(import groovy.time.TimeCategory; def now = new Date(); use(TimeCategory) { def nowPlusOneYear = now + 12.month - 1.day; return URLEncoder.encode(nowPlusOneYear.format('dd/MM/YYYY')\, 'UTF-8')},Policy_ExpiryDate)}
The above code works well with a JSR223 Sampler. Also, the variable name and its value gets displayed in debug sampler.
But, when I use this code along with a GET HTTP Request. The value doesn't gets substituted.
the request:
/IIMS/target/source/UNDERWRITING/ValidatorAction.action?dataString=%7B%22Testing%22%3A%22F%22%2C%22VCURRENTSTATUSNAME%22%3A%22%22%2C%22Entry%20Date%22%3A%22${__urlencode(${__time(dd/MM/YYYY,Entry_Date)})}%22%2C%22Policy%20Type%22%3A%22FLEET%22%2C%22Policy%20Inception%20Date%22%3A%22${__urlencode(${__time(dd/MM/YYYY,Policy_InceptionDate)})}%22%2C%22Policy%20Inception%20Time%22%3A%22${__urlencode(${__time(HH:mm:ss,Policy_InceptionTime)})}%22%2C%22Policy%20Duration%22%3A%2212%22%2C%22Unit%22%3A%22F%22%2C%22Policy%20Expiry%20Date%22%3A%22${__groovy(import groovy.time.TimeCategory; def now = new Date(); use(TimeCategory) { def nowPlusOneYear = now + 12.month - 1.day; return URLEncoder.encode(nowPlusOneYear.format('dd/MM/YYYY')\, 'UTF-8')},Policy_ExpiryDate)}%22%2C%22Policy%20Expiry%20Time%22%3A%2223%3A59%3A59%22%2C%22Type%20Of%20Business%22%3A%22CASH%22%2C%22Payment%20Frequency%22%3A%22B%22%2C%22Country%22%3A%22UAE%22%2C%22Source%20of%20Business%22%3A%220DIR%22%2C%22Policy%20branch%22%3A%22${UserDetails_g5}%22%2C%22UMR%20Number%22%3A%22%22%2C%22Equator%20Policy%22%3A%22N%22%2C%22Policy%20holder%22%3A%22${PolicyHolderDetails_g1}%22%2C%22Policy%20holderNew%22%3A%22${PolicyHolderDetails_g2}%22%2C%22SEC-POLPLANNumber%20of%20Vehicles%20in%20Fleet%22%3A%220%22%2C%22SEC-POLPLANPolicy%20Plan%22%3A%22NA%22%2C%22SEC-POLPLANDistribution%20Channel%22%3A%22DIROIC%22%2C%22SEC-POLPLANName%20of%20the%20Scheme%22%3A%22NA%22%2C%22SEC-PLRIDPolicy%20Level%20FAC%20R%2FI%22%3A%22N%22%2C%22SEC-PLRIDPolicy%20Level%20FAC%20R%2FI%20percentage%22%3A%220%22%2C%22SEC-PLRIDPolicy%20Level%20Proportional%20FAC%20R%2FI%20percentage%22%3A%220%22%2C%22SEC-PLRIDPolicy%20Level%20Non%20Proportional%20FAC%20R%2FI%20percentage%22%3A%220%22%2C%22SEC-PLRIDProp%20Non%20Prop%20FAC%20Date%22%3A%22Y%22%2C%22SEC-CEDANTNumber%20of%20Cedants%20Involved%22%3A%22%22%2C%22SEC-CEDANTCedant%20Country%22%3A%22%22%2C%22SEC-MDPMinimum%20Deposit%20Premium%20Applicable%22%3A%22N%22%2C%22SEC-MDPMinimum%20Deposit%20Premium%20Type%22%3A%22%22%2C%22SEC-MDPPercentage%20of%20Premium%22%3A%22%22%2C%22SEC-MDPMinimum%20Deposit%20Premium%22%3A%22%22%2C%22SEC-MDPMinimum%20premium%22%3A%22%22%2C%22SEC-CRMCRM%20Reference%20Number%22%3A%22${__Random(1111111,9999999,CRM_RefNo)}%22%2C%22pBusinessTranCode%22%3A%22SCR-BSCDTL%22%2C%22pSaveContinueIndicator%22%3A%22%22%2C%22pJSPName%22%3A%22BasicInformation.jsp%22%2C%22crtPartyFunctionInd%22%3A%22Y%22%2C%22strTOC%22%3A%22%22%2C%22existingPartyIndicator%22%3A%22Y%22%2C%22disabledSectionCode%22%3A%22%22%2C%22scriptaculous%22%3A%22%22%2C%22language%22%3A%22null%22%2C%22policyId%22%3A%22null%22%2C%22policyPlanUpgradation%22%3A%22N%22%2C%22reason%22%3A%22%22%2C%22isMasterQuote%22%3A%22FALSE%22%2C%22USERCODE%22%3A%22${UserDetails_g6}%22%2C%22pVersionDate%22%3A%22%22%2C%22__endorsementType%22%3A%22null%22%2C%22cSystemDate%22%3A%22${__urlencode(${__time(dd/MM/YYYY,cSystemDate)})}%22%2C%22cedantEndrDate%22%3A%2208%2F12%2F2117%22%2C%22CIMS_CSRFTOKEN%22%3A%22${CIMS_CSRFTOKEN}%22%7D%3B&productCode=0101&productId=1030885614052014
All values get substituted properly except for the groovy part. Am I missing something over here. The debug sampler doesn't shows a variable named Policy_ExpiryDate.
I cannot reproduce your issue, __groovy() function is normally getting evaluated and the substituted by its respective value:
If it doesn't for you - most probably it fails somewhere somehow, check jmeter.log file for any suspicious entries
Also you can execute the function anywhere else, for example in User Defined Variables configuration element and refer the value where required just as ${Policy_ExpiryDate}
__groovy() works well with Jmeter 5.4. But this function is not recognized in Jmeter 5.3.
Thanks a lot for your help #Dmitri T
Regards,
Ajith

F Strings and Interpolation using a properties file

I have a simple python app and i'm trying to combine bunch of output messages to standardize output to the user. I've created a properties file for this, and it looks similar to the following:
[migration_prepare]
console=The migration prepare phase failed in {stage_name} with error {error}!
email=The migration prepare phase failed while in {stage_name}. Contact support!
slack=The **_prepare_** phase of the migration failed
I created a method to handle fetching messages from a Properties file... similar to:
def get_msg(category, message_key, prop_file_location="messages.properties"):
""" Get a string from a properties file that is utilized similar to a dictionary and be used in subsequent
messaging between console, slack and email communications"""
message = None
config = ConfigParser()
try:
dataset = config.read(prop_file_location)
if len(dataset) == 0:
raise ValueError("failed to find property file")
message = config.get(category, message_key).replace('\\n', '\n') # if contains newline characters i.e. \n
except NoOptionError as no:
print(
f"Bad option for value {message_key}")
print(f"{no}")
except NoSectionError as ns:
print(
f"There is no section in the properties file {prop_file_location} that contains category {category}!")
print(f"{ns}")
return f"{message}"
The method returns the F string fine, to the calling class. My question is, in the calling class if the string in my properties file contains text {some_value} that is intended to be interpolated by the compiler in the calling class using an F String with curly brackets, why does it return a string literal? The output is literal text, not the interpolated value I expect:
What I get The migration prepare phase failed while in {stage_name} stage. Contact support!
What I would like The migration prepare phase failed while in Reconciliation stage. Contact support!
I would like the output from the method to return the interpolated value. Has anyone done anything like this?
I am not sure where you define your stage_name but in order to interpolate in config file you need to use ${stage_name}
Interpolation in f-strings and configParser files are not the same.
Update: added 2 usage examples:
# ${} option using ExtendedInterpolation
from configparser import ConfigParser, ExtendedInterpolation
parser = ConfigParser(interpolation=ExtendedInterpolation())
parser.read_string('[example]\n'
'x=1\n'
'y=${x}')
print(parser['example']['y']) # y = '1'
# another option - %()s
from configparser import ConfigParser, ExtendedInterpolation
parser = ConfigParser()
parser.read_string('[example]\n'
'x=1\n'
'y=%(x)s')
print(parser['example']['y']) # y = '1'

Creating automatic folder and files in soapui

I wrote a groovy script in soapui to create files in certain location in my pc. How can I make it dynamic and enable the user to write the location the files are saved to by write the location in configuration file imported at test suite level.
if(context.expand('${#Project#ProduceReports}') == 'true') {
def resultDir = new File("D:\\Reports");
if(!resultDir.exists()) {
resultDir.mkdirs();
}
def resultsFile = new File(resultDir, "CSVReport.csv");
}
If you want to get the path from a testSuite property, you can do it as you do with the project property, using context.expand:
def yourPath = context.expand('${#TestSuite#pathDirectory}')
Or alternatively you can do the same with:
def yourPath = context.testCase.testSuite.getPropertyValue('pathDirectory')
Maybe this is out of scope for your question, but could be helpful. If you need you can also use UISupport to ask the user to enter the path he wants with the follow code:
def ui = com.eviware.soapui.support.UISupport;
// the prompt question, title, and default value
def path = ui.prompt("Enter the path","Title","/base/path");
log.info path
This shows:
Define project level custom property REPORT_PATH with value D:/Reports/CSVReport.csv i.e., full path including file and path separate by / slash even on windows platform.
Then use the below script to write the data.
//Define the content that goes as report file. Of course, you may change the content as need by you
def content = """Name,Result
Test1,passed
Test2,failed"""
//Read the project property where path is configured
def reportFileName = context.expand('${#Project#REPORT_PATH}')
//Create file object for reports
def reportFile = new File(reportFileName)
//Create parent directories if does not exists
if (!reportFile.parentFile.exists()) {
reportFile.parentFile.mkdirs()
}
//Write the content into file
reportFile.write(content)

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

SoapUI Mock Service Custom Sequence of Responses

I have a SoapUI Mock Service with multiple responses. I want to define a custom sequence for my responses and I do not necessarily use all of the responses in this particular test.
I have actually managed to get this to work in the past, but I think something changed in a newer version of the product and the feature stopped working. That was with a SOAP web service. Now I am mocking a RESTful web service and I have the same requirement to help me do my tests.
The SEQUENCE dispatch option is not what I want, because it will return all of the defined responses in the order in which they were created. The SCRIPT option is what I used previously but now all I can achieve with this is define one response to be generated. For this test I have no interest in examining some content of the request to decide which response to send back.
For example, if I have 8 responses defined, I just want to be able to specify that the following responses are returned:-
Response #2, then Response #3, then Response #4, then finally Response #7; so that Responses #1, #5, #6 and #8 are not used.
My question is posed in detail in the SmartBear forum here:-
simple scripting in a Mock Service - no longer works
I try as you post in the SOAPUI forum using consecutive returns statements with the response order an it doesn't work.
Instead of your groovy code as a DISPATCH script I purpose to use the follow groovy code as workaround, which consist in a use of a list to keep your responses in the desired order and keeping this list in the context an updating it each time using the following code:
// get the list from the context
def myRespList = context.myRespList
// if list is null or empty reinitalize it
if(!myRespList || !myRespList?.size){
// list in the desired output order using the response names that your
// create in your mockservice
myRespList = ["Response 2","Response 3","Response 4","Response 7"]
}
// take the first element from the list
def resp = myRespList.take(1)
// update the context with the list without this element
context.myRespList = myRespList.drop(1)
// return the response
log.info "-->"+resp
return resp
This code works as you expect, since context is keeping the list and each time this script returns the next response and when the list is empty it repopulate it an restart the loop again in the same order.
As an illustration when I use this mockService I get the follow script log:
EDIT
If as OP you've problems with your SOAPUI version because the returned string is between square brackets as ie: [Response 1], change the way that element is taken from the array using:
// take the first element from the list
def resp = myRespList.take(1)[0]
instead of:
// take the first element from the list
def resp = myRespList.take(1)
Note the [0].
With this change the return string will be Response 1 instead of [Response 1].
In this case the script will be:
// get the list from the context
def myRespList = context.myRespList
// if list is null or empty reinitalize it
if(!myRespList || !myRespList?.size){
// list in the desired output order using the response names that your
// create in your mockservice
myRespList = ["Response 2","Response 3","Response 4","Response 7"]
}
// take the first element from the list
def resp = myRespList.take(1)[0]
// update the context with the list without this element
context.myRespList = myRespList.drop(1)
// return the response
log.info "-->"+resp
return resp
Hope this helps,

Resources