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 🙂

18 Comments »

  1. set value?

    Comment by Anonymous — October 14, 2015 @ 1:42 pm | Reply

  2. Hi Siking,
    I have a problem with groovy with the same situation as you point above. Can I mail you about it?
    ps: I can not find your mail in this site.
    Dennis

    Comment by Orrin — September 23, 2015 @ 4:31 am | Reply

  3. Hi Siking,

    Ik get this error: java.lang.NullPointerException: Cannot invoke method getNodeValue() on null object java.lang.NullPointerException: Cannot invoke method getNodeValue() on null object error at line: 30

    at this piece of code:

    responseHolder.getDomNodes(“//*:hotel/*:roomType/*:guestCount[1]/*:night”).each {

    What might be the problem

    Comment by Orrin — September 11, 2015 @ 7:25 am | Reply

    • Sounds like you are trying to get value of a node that does not exist. From that piece of code it is impossible to tell.

      Comment by SiKing — September 11, 2015 @ 7:31 am | Reply

  4. […] dynamically emanate elements in a SoapUI ask | SiKing […]

    Pingback by SoapUI – Automaticaly add custom SOAP headers to outgoing request | Zale Answers — December 11, 2014 @ 4:37 pm | Reply

  5. When I try to use the same code as mentioned above , still getting the error as Getting the error:
    Groovy.lang.MissingMethodException: No signature of method: [Lorg.w3c.dom.Node;.getOwnerDocument() is applicable for argument types: () values: []

    Checked the jar is there in the lib directory, moved it to ext folder also, still no luck.
    what should i do to resolve the same..?

    Comment by Anand — September 27, 2014 @ 3:21 am | Reply

    • This does not involve any external jars. All jars should be part of the SoapUI installation.

      Comment by SiKing — September 29, 2014 @ 7:46 am | Reply

  6. 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

  7. 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

  8. 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

  9. 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 comment

Blog at WordPress.com.