Pages

Sunday, June 16, 2013

SoapUI: Inserting an external XML node dynamically into an existing XML Response using XmlHolder

The Problem: I have a Soap Response, and I have to modify that xml such that I insert a new XML node into response. The new XML is a string that I can declare in a groovy script or I can retrieve from a property.

Few exceptions I encountered while trying to solve the problem: CData missing, can not add external document, Can not add groovy Node class to java Node class, viceversa and etc etc etc

Hints to solve the problem on internet: There are many websites that provides sample which helps to solve a particular case, but could not exactly find the one I present here. The most useful one was here "http://siking.wordpress.com/2012/01/06/dynamically-create-elements-in-a-soapui-request/" where in the coments he gives sample related to owner doc and importing into it.

Details:

Lets say you have a soap request and response in a test step of a test case. The body of the response is like below.
<Traveler xmlns="http://www.example.com/schemas">
<Customer BirthDate="2001-01-01">
<Person Language="EN-US">
<Title>MR</Title>
<FirstName>SHRE</FirstName>
<MiddleName>NOWVERYMUCH</MiddleName>
<LastName>WANTED</LastName>
</Person>
<Email Type="HOM" Address="notvalid@notvalid.com"/>
</Customer>
</Traveler>

And I want add a new XML node Payment to Customer. Lets say his payment information like credit card number which is an XML fragment like below. Later I will add the card number to it.

def pay="""<Payment>
<Card Type="INTERNATIONAL" Vendor="VISA" Number="">
<Name>
Huntsville Townhall
</Name>
<Issuer Name="Bank of Nowhere"/>
</Card>
</Payment>"""

Create a new property transfer from Traveler xml in the Soap Response to copy the Customer node to a TestCase property "customer". You can do that by using the below configuration in a new Propperty transfer step after the Soap RQ/RS step.

Source: SoapRequestStepName Property: Response
declare namespace ns1='http://www.example.com/schemas';
//ns1:Traveler//ns1:Customer
Target: TestCaseName Property: nodeCustomer


Add a new test step with groovy script called updatePaymentInXML. First step is to create a XmlHolder for the source XML Traveler and extract Customer node out of it.
def getXmlHolder(){
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context )
def nodeCustStr=testRunner.testCase.getProperty("nodeCustomer").getValue()
log.info "Pre Update: " + nodeCustStr
def holder = groovyUtils.getXmlHolder(nodeCustStr)
holder.namespaces["ex"] ="http://www.example.com/schemas"
return holder
}

Next lets get the Customer node from it as a Node.

def holder = getXmlHolder()
def nodeCustomer = holder.getDomNode("//ex:Customer")

Next lets insert payment in the customer node.

def insertPaymentInCustomer(holder, nodeCustomer, pay){
def ownerDoc=nodeCustomer.getOwnerDocument()
def payHolder=new com.eviware.soapui.support.XmlHolder(pay)
payHolder.namespaces["ex"] ="http://www.example.com/schemas"
def nodePay = payHolder.getDomNode("//ex:Payment")

def importedNodePay=ownerDoc.importNode(nodePay, true)
nodeCustomer.appendChild(importedNodePay)
holder.updateProperty()
log.info "Post Update: " + holder.getXml()
}
insertPaymentInCustomer(holder, nodeCustomer, pay)

Things to note in the above snippet is that we get the owner document and import the newly extracted Payment xml node from the xml string using a new XmlHolder instance. Lets add the card number to it.

def updateCCNumberInHolder(holderIn, ccNumberIn){
holderIn["//ex:Card/@Number"]=ccNumberIn
holderIn.updateProperty()
testRunner.testCase.setPropertyValue("nodeCustomerUpdated",holderIn.getXml())
log.info "Post Update: " + holderIn.getXml()
}
updateCCNumberInHolder(holder, testRunner.testCase.getPropertyValue("ccNumber"))

Now, the holder.getXml() gives you the latest XML with updated Payment and cc number in it. Test case property nodeCustomerUpdated has the all updated XML that you can use.