How to extract value from a string in XML response using karate - dsl

Unfortunately the respons ei am getting from my backend is not in correct xml format and it's giving the response in a bad format like this:
<soapenv:Body>
<ns2:getInputResponse xmlns:ns2="http://docs.oasisopen.org/ns/bpel4people/ws-humantask/api/200803">
<ns2:taskData xmlns:s186="http://www.w3.org/2001/XMLSchema-instance" xmlns:s187="http://www.w3.org/2001/XMLSchema" s186:type="s187:string"><?xml version="1.0" encoding="UTF-8"?>
<SubscriptionApprovalData xmlns="http://workflow.subscription.apimgt.carbon.wso2.org" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<apiName>Auto_Approval</apiName>
<apiVersion>v1</apiVersion>
<apiContext>/test/lambda/v1</apiContext>
<apiProvider>admin</apiProvider>
<subscriber>regtest</subscriber>
<applicationName>newApp</applicationName>
<tierName>Gold</tierName>
<workflowExternalRef>23d30bd8-51e3-4afe-aae0-3fa159d85a6b</workflowExternalRef>
<callBackURL>https://apistore-dev-dev-a878-14-ams10-nonp.qcpaws.qantas.com.au/services/WorkflowCallbackService</callBackURL>
</SubscriptionApprovalData></ns2:taskData>
</ns2:getInputResponse>
</soapenv:Body>
Now because of this Karate is not able to read the response and fetch the value of "workflowExternalRef" which is my goal for this test.
Is there any way karate can read it?

This is really messed up XML so please check with someone in your team if this can be fixed.
Anyway, since you can use Java in Karate, here is one way to do this. This is not production-quality code, please adapt as appropriate:
* string response = response
* def start = response.indexOf('workflowExternalRef>')
* def ref = response.substring(start + 23)
* def end = ref.indexOf('<')
* def ref = ref.substring(0, end)
* match ref == '23d30bd8-51e3-4afe-aae0-3fa159d85a6b'

Related

XML Element Tree - appending to existing elements and attributes with ET.SubElement()?

I have the following function which builds up a re-usable XML SOAP envelope:
def get_xml_soap_envelope():
"""
Returns a generically re-usable SOAP envelope in the following format:
<soapenv:Envelope>
<soapenv:Header/>
<soapenv:Body />
</soapenv:Envelope>
"""
soapenvEnvelope = ET.Element('soapenv:Envelope')
soapenvHeader = ET.SubElement(soapenvEnvelope, 'soapenv:Header')
soapenvBody = ET.SubElement(soapenvEnvelope, 'soapenv:Body')
return soapenvEnvelope
Fairly simple stuff so far.
I was wondering now, would it be possible to append attributes (such as xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance") to the soapenv:Envelope element?
And if I also wanted to append the following XML:
<urn:{AAction} soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<AUserName>{AUserName}</AUserName>
<APassword>{APassword}</APassword>
</urn:{AAction}>
To the soapenv:Body such that I would have something like this:
if __name__ == "__main__":
soapenvEnvelope = get_xml_soap_envelope()
actions = {
'AAction': 'UserLogin',
}
soapAAction = ET.Element('urn:{AAction}'.format(**actions))
soapenvEnvelope.AppendElement(soapAAction, 'soapenv:Body')
So, I could specify a target node and the Element to append to?
Let's start from the bad news: Your function to create the SOAP envelope
(get_xml_soap_envelope) is wrong as it fails to specify at least
xmlns:soapenv="...".
Actually all other namespaces to be used should be also specified here.
A proper function creating the SOAP envelope should be somenting like this:
def get_xml_soap_env():
"""
Returns a generically re-usable SOAP envelope in the following format:
<soapenv:Envelope xmlns:soapenv="...", ...>
<soapenv:Header/>
<soapenv:Body />
</soapenv:Envelope>
"""
ns = {'xmlns:soapenv': 'http://schemas.xmlsoap.org/soap/envelope/',
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'xmlns:urn': 'http://dummy.urn'}
env = ET.Element('soapenv:Envelope', ns)
ET.SubElement(env, 'soapenv:Header')
ET.SubElement(env, 'soapenv:Body')
return env
Note that ns dictionary contains also other namespaces, which will be
needed later, a.o. xsi namespace.
A possible alternative is to define ns outside of this function and pass it as
a parameter (your choice).
When I ran:
env = get_xml_soap_env()
print(ET.tostring(env, encoding='unicode', short_empty_elements=True))
the printout (reformatted by me for readability) was:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:urn="http://dummy.urn"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header />
<soapenv:Body />
</soapenv:Envelope>
Note that this time proper namespaces are included.
Then, to add the Action element and its children, define the following function:
def addAction(env, action, subelems):
body = env.find('soapenv:Body')
actn = ET.SubElement(body, f'soapenv:{action}')
for k, v in subelems.items():
child = ET.SubElement(actn, k)
child.text = v
When I ran:
subelems = {'AUserName': 'Mark', 'APassword': 'Secret!'}
addAction(env, 'UserLogin', subelems)
and printed the whole XML tree again, the result was:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:urn="http://dummy.urn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header />
<soapenv:Body>
<soapenv:UserLogin>
<AUserName>Mark</AUserName>
<APassword>Secret!</APassword>
</soapenv:UserLogin>
</soapenv:Body>
</soapenv:Envelope>

Jmeter PostProcessor with Groovy and fetching content of response data

In JSR223 PostProcessor I am using this method to get the response data:
def json = new JsonSlurper().parseText(response)
Here is a snippet of my json output is like this
XmlItemResult:[[xPath:/Blocks/Block[001], name:abc, folder:\A\abc\a1, id:84, information:[[xPath:/Blocks/Block[001], result:Block number 1: abc], [xPath:/Blocks/Block[001]/Steps/CallSteps/Step[001], result:Call step StepNo 1],
folder:\Voice133, id:2542, information:[[xPath:/TestCases/TestCase[001],
This response, as you see contains two things which I am interested in:
folder:\A\abc\a1, id:84,
folder:\Voice133, id:2542,
I need to get the id value for only this line --> folder:\Voice133, id:2542,
note 2542 is variable and can be different each time and after each run.
I tried
json.find ("Voice133, id:(.+?),")
Your string is not a valid JSON, you can check it yourself using any online JSON validator, therefore you won't be able to use JsonSlurper, you will have to go for Regular Expressions instead.
In Groovy you can use =~ - Find Operator in order to be able to extract the required value(s), example code would be something like:
def response = 'XmlItemResult:[[xPath:/Blocks/Block[001], name:abc, folder:\\A\\abc\\a1, id:84,' +
' information:[[xPath:/Blocks/Block[001], result:Block number 1: abc],' +
' [xPath:/Blocks/Block[001]/Steps/CallSteps/Step[001], result:Call step StepNo 1], '
def matcher = (response =~ 'folder:\\\\A\\\\abc\\\\a1, id:(\\d+),')
if (matcher.find()) {
log.info('Folder ID = ' + matcher.group(1))
}
Demo:
More information: Apache Groovy - Why and How You Should Use It

soapUI groovy script groovy.lang.MissingMethodException

Following exception is received when I tried to parse response within a soapUI test step.Also tried getXMLHolder method. Still no luck.
Am I missing an import or library?
groovy.lang.MissingMethodException: No signature of method:
java.lang.String.getNodeValue() is applicable for argument types:
(java.lang.String) values:
[//ConversionRateResponse/ConversionRateResult] error at line: 16
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context);
project = testRunner.getTestCase().getTestSuite().getProject().getWorkspace().getProjectByName("FirstProject")
testSuite = project.getTestSuiteByName("TestSuite 1");
testCase = testSuite.getTestCaseByName("TestCase 1");
testCase.setPropertyValue("fromCurrency","EUR")
testCase.setPropertyValue("toCurrency","TRL")
testStep=testCase.testSteps["SOAP Request1"]
def responseHolder=testStep.getPropertyValue("response");
def refNum = responseHolder.getNodeValue("//ConversionRateResponse/ConversionRateResult")
And the response is as follows
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<ConversionRateResponse xmlns="http://www.webserviceX.NET/">
<ConversionRateResult>-1</ConversionRateResult>
</ConversionRateResponse>
</soap:Body>
</soap:Envelope>
You can add the Script Assertion to the Soap Request Test step.
Here is the script:
//Check if the response is not empty
assert context.response, 'Response is empty or null'
def rate = new XmlSlurper().parseText(context.response).'**'.find{it.name() == 'ConversionRateResult'}?.text() as Integer
log.info "Conversion rate result is : $rate "
//Check if the result rate is -1, change if needed
assert -1 == rate
I can see you have used getNodeValue but on String which is wrong
if you see your error it says, "No signature of method: java.lang.String.getNodeValue() is applicable for argument types: (java.lang.String) values"
see the below code where we have used the getNodeValue on the correct thing
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context);
def response = groovyUtils.getXmlHolder('SOAP Request#Response')
def refNum=response.getNodeValue("//*:ConversionRateResponse//*:ConversionRateResult")
log.info refNum
getNodeValue is a very useful function and will help a lot in extracting value from xml, Similarly we have getDomNode which is for the nodes and not values

soapui free version assertions request = response

I will pass any memberid or active card for matching to verify activecard(cardno) matches memberid passed.
Request:
<MemberID>${Property Looper#memberid}</MemberID>
<CardNo>${Property Looper#ActiveCard}</CardNo>
Expected result:
<ReturnMessage>Cardno not found</ReturnMessage>
OR
<ReturnMessage>SUCCESS</ReturnMessage>
How to put assertions to check if the member id in request will check matching with response? contains and not contains assertions seems like not working that well for this condition?
and i want the test to be passed even if the matching failed as the ultimate goal is to make sure the error is not from the data (the test pass regardless the return status) but the application. Thanks
Edit:
after running the step
Edit 2:
after running script assertion
custom property added
You can use Script Assertion to achieve the same.
Based on the description, the assertion should be able to handle both below cases :
some case, you may expect card not found
some case, you may expect SUCCESS
Define test case level custom property, say EXPECTED_MESSAGE and provide appropriate expected value depending on the case.
Add below script assertion for the SOAP Request test step.
//Check if the response is received
assert context.response, 'Response is empty or null'
//Parse the response and find the ReturnMessage value
def actualMessage = new XmlSlurper().parseText(context.response).'**'.find{it.name() == 'ReturnMessage'}?.text()
log.info "Return message from response: $actualMessage"
//Read the expected message from custom property
def expectedMessage = context.expand('${#TestCase#EXPECTED_MESSAGE}')
//Verify with actual value
assert actualMessage == expectedMessage
Let us assume the output is like
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<ReturnMessage>SUCCESS</ReturnMessage>
</soap:Body>
</soap:Envelope>
you can use the below script assertion
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def response = groovyUtils.getXmlHolder("First Step#Response")
String x = response.getNodeValue("//*[local-name()='ReturnMessage']")
if(x.contains("Cardno not found" ) || x.contains("SUCCESS"))
{
log.info "Success"
}
else
{
log.info " Fail"
}
output is
Sat Oct 28 15:22:02 IST 2017:INFO:Success
Rao's approach is also correct. Its just that if you are following that approach you have to have 2 expected result custom properties since you have 2 properties.
You may have to use if condition before assertion as 1 assertion will fail because your results would be either "Cardno not found" or "SUCCESS"
2 Changes you might have to make in the script. replace 'First Step' with the name of your teststep
change the getNodeValue step to reach the actual place

How to get all the XML nodes and its values from a XML with groovy?

I have an XML like below.
xml1 = '''
<?xml version="1.0" encoding="UTF-8"?>
<soap>
<group1>
<g1node1>g1value1</g1node1>
<g1node2>g1value2</g1node2>
<g1node3>g1value3</g1node3>
</group1>
<group2 attr="attrvalue1">
<g2node1>g2value1</g2node1>
<g2node2>g2value2</g2node2>
<g2node3>g2value3</g2node3>
</group2>
</soap>
'''
Here, i need to get all the xml node and its values as output, either as line by line result and as a list with groovy. The output should look like
g1node1 = g1value1
g1node2 = g1value2
... and so on...
or either with a groovy map like below
out = [g1node1 : "g1value1", g1node2 : "g1value2", ...and so on...]
can anyone help me how to achieve this with groovy code?
I would still like to know (as Tim mentioned) what you have tried yet, but here is my itch:
def result = new XmlSlurper().parseText( xml )
result.'**'.collectEntries { !it.childNodes() ? [ it.name(), it.text() ] : [:] }

Resources