SiKing

January 6, 2012

dynamically create elements in a SoapUI request

Filed under: automation — SiKing @ 11:44 am
Tags:

I got this problem – took me like 8 hours to solve, so that qualifies it as a problem. In SoapUI I have a call that responds with something like <night ratePlanID="A" stayDate="B" availCodeID="C" regularPrice="D" totalPrice="E" />. There are multiple of these, the number can change depending on the inputs to this call. I need to create the same number of elements for my next call, and change the attributes around a bit to make it look something like: <hot:night ratePlanID="A" resDate="B" quotedPrice="E" />. Luckily Groovy abstracts away the need for counting things, and I naively thought that manipulating the XML would be a breeze too…

version 1.0

The first useful hints that I got were from the SoapUI tips & tricks. After that getting at the individual attributes is done with attributes.getNamedItem(). After that I just brute-forced my way through it.

// create groovyUtils and XmlHolder for response of my "find room" call
def grUtils = new com.eviware.soapui.support.GroovyUtils(context)
def holder = grUtils.getXmlHolder("find room#Response")

// place for my final result
def entireXmlFragment = new StringBuilder()

// get only the nodes that I am interested in, and iterate over all of them
holder.getDomNodes("//*:hotel/*:roomType/*:guestCount[1]/*:night").each {

	// create a new node one piece of string at a time
	def oneXmlFragment = new StringBuilder()
	oneXmlFragment << "<hot:night ratePlanID='"
	oneXmlFragment << it.attributes.getNamedItem("ratePlanID").getNodeValue()
	oneXmlFragment << "' resDate='"
	oneXmlFragment << it.attributes.getNamedItem("stayDate").getNodeValue()
	oneXmlFragment << "' quotedPrice='"
	oneXmlFragment << it.attributes.getNamedItem("totalPrice").getNodeValue()
	oneXmlFragment << "' />"
	oneXmlFragment << System.getProperty("line.separator")

	// add the new node to my final result
	entireXmlFragment << oneXmlFragment
}

// push the final result into a property
testRunner.testCase.setPropertyValue("entireXmlFragment", entireXmlFragment.toString())

Now in my next call I just insert ${#TestCase#entireXmlFragment} where I want this XML fragment to be placed. Works, but seems kinda neanderthal.

version 2.0

There has to be a way to manipulate my XmlFragments as XML objects, and there has to be a way to push them directly into the next call? The grUtils.getXmlHolder() led me to holder.getDomNodes(), which gives you back a org.w3c.dom.Node.

// create groovyUtils and XmlHolder for response of my "find room" call
def grUtils = new com.eviware.soapui.support.GroovyUtils(context)
def responseHolder = grUtils.getXmlHolder("find room#Response")

// create XmlHolder for request of my "bookHotelRes" call
def requestHolder = grUtils.getXmlHolder("bookHotelRes#Request")
// find the Node that I am interested in
def requestNode = requestHolder.getDomNode("//*:bookHotelResInput/*:room")
// the Document object is used to create new nodes
def requestDoc = requestNode.getOwnerDocument()

// get only the nodes that I am interested in, and iterate over all of them
responseHolder.getDomNodes("//*:hotel/*:roomType/*:guestCount[1]/*:night").each {

	// create a new Element in the Document
	def oneElement = requestDoc.createElementNS(requestNode.getNamespaceURI(), "night")
	// define all the attributes
	oneElement.setAttribute("ratePlanID", it.attributes.getNamedItem("ratePlanID").getNodeValue())
	oneElement.setAttribute("resDate", it.attributes.getNamedItem("stayDate").getNodeValue())
	oneElement.setAttribute("quotedPrice", it.attributes.getNamedItem("totalPrice").getNodeValue())
	// insert the Element
	requestNode.insertBefore(oneElement, requestNode.getFirstChild())
}

// write the Document out to the request
requestHolder.updateProperty(true)

The above will write out the next Element as the first child. Also, and perhaps more importantly, the above will write it out into your request – if you run your test twice in a row, you will end up with both results (old and new) in your request. This is undesirable, but easy to take care of.

// cleanup from a "previous" run
requestHolder.getDomNodes("//*:bookHotelResInput/*:room/*:night").each {
	requestNode.removeChild(it)
}

HTH :)

About these ads

11 Comments »

  1. Getting the error:
    Groovy.lang.MissingMethodException: No signature of method: [Lorg.w3c.dom.Node;.getOwnerDocument() is applicable for argument types: () values: [] error at line: 33

    I am new to SOAPUI and groovy. Could you please help me with this error?

    Comment by Kanchan — June 18, 2012 @ 1:38 am | Reply

    • I would need to see much more of your code to be able to perhaps take a guess. Perhaps one of the support fora would be a better medium for this?

      Comment by siking — June 18, 2012 @ 7:01 am | Reply

      • This is the code:
        Pretty much the same thing as you have written

        def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
        def holder = groovyUtils.getXmlHolder(“matchRanking – Request 1#Request”)
        def node = holder.getDomNodes(“//ran:matchRanking”)

        def nodeCount = node.length
        log.info “nodeCount = ” + nodeCount

        def requestDoc = node.getOwnerDocument()
        def oneElement = requestDoc.createElementNS(node.getNamespaceURI(), “candidatePropsList”)

        def childNode = holder.getDomNodes(“//candidatePropsList”)
        def childNodeCount = childNode.length
        log.info “childNodeCount = ” + childNodeCount

        Comment by Kanchan — June 18, 2012 @ 10:18 pm | Reply

        • I do not see anything immediately wrong in the code. However, I do not see “line 33″ either.

          Comment by siking — June 19, 2012 @ 8:45 am | Reply

          • Ignore the line number.. I made some changes, so the line number has changed.
            Basically the error is for getOwnerDocument().
            Do I need to include some files for this to work? since it says “MissingMethodException”

            Comment by Kan — June 19, 2012 @ 7:35 pm

          • The getOwnerDocument() method is declared in $SOAPUI_HOME/lib/xml-apis-2.9.1.jar. I do not have any special imports for my code.

            Comment by siking — June 20, 2012 @ 1:39 pm

  2. jQuery & Sizzle have similar regex expressions.

    Comment by Karaoke_King — April 26, 2012 @ 8:36 pm | Reply

    • Thanx. However, there is not a single regular expression in the above post. Perhaps you mean XPath?

      Comment by siking — April 27, 2012 @ 7:08 am | Reply

      • Yes, exactly.

        Comment by Anonymous — July 2, 2012 @ 3:30 pm | Reply

  3. Copying a node from one document to another, with no modifications:

    def segmentNode = flightHolder.getDomNode("//*:journeySet[1]/*:journey[1]/*:segment")
    def importNode = bookDoc.importNode(segmentNode, true)
    bookNode.insertBefore(importNode, bookNode.getFirstChild())
    

    Comment by siking — April 11, 2012 @ 9:50 am | Reply

  4. Adding text is a child node itself, and it is done with something like:

    def discountElement = requestDoc.createElementNS(requestNode.getNamespaceURI(), "element_name")
    oneNode.appendChild(discountElement)	// oneNode is the parent
    discountElement.appendChild(requestDoc.createTextNode("text to add"))
    

    Comment by siking — January 9, 2012 @ 10:24 am | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: