XPath in soapUI Part 1: XPath Assertions and Namespaces

After a long hiatus for the American holidays, in response to numerous requests I'm going to look at using XPath in soapUI over the next two or three posts.  In this post we'll walk through creating a very basic XPath assertion and discuss namespaces, which almost always pop up but can be a bit tricky and not very intuitive if you're new to XPath.  For the sake of space and time, this may seem like a bit of a whirlwind introduction, but I'm hoping it will be more than enough to get you started.  For an in-depth look at XPath or to fill in the gaps, I recommend the tutorial at w3schools.com.

You can download a zipped sample project here with several request steps for which you can create and experiment with your own XPath assertions.  The service returns information about holidays in several countries; e.g., it returns a list of holidays in a given year, the date of a given holiday, etc.  Also, it doesn't use CDATA-- as I discussed in previous posts, services that make extensive use of CDATA require some extra scripting to get them to work with XPath.  Download the project, unzip it, and import it into your soapUI workspace (if you want to create your own project from scratch, the WSDL for this services is at http://www.holidaywebservice.com/Holidays/HolidayService.asmx?WSDL).

Let's walk through creating a simple XPath assertion.  Once you've imported the project and opened it in soapUI, run the GetHolidayDate test request in the GetHolidayDate test case; the response should look something like this:


GetHolidayDate Response XML

SoapUI's XPath assertions allow you to confirm that a specified value in your test request's response XML matches some expected value.  Let's create an assertion that verifies the request is returning the expected date-- in other words, that the value of the GetHolidayDateResult element is '2013-01-01T00:00:00'.  Open the test step editor and expand the Assertions panel, if necessary (click on the Assertions button at the bottom of the editor window).  Open the Add Assertion dialog; the XPath Match assertion is available in the Property Content group.


XPath Match in the Add Assertion dialog

After selecting the XPath Match assertion type, the XPath Match Configuration dialog is launched:


XPath Match Configuration dialog

Once in this dialog, click on the Declare button above the top panel-- soapUI scans the test response to try to identify any namespaces used and automatically creates corresponding declarations:


Namespace declarations automatically generated by soapUI

For those of you unfamiliar with namespaces (and those of you who are may want to skim the next few paragraphs), these provide a way to avoid naming conflicts in XML.  For example, you may have two web services that both return an element named "Addressee".  Associating each "Addressee" element with a different namespace ensures that each remains unique.  Namespaces are generally defined within XML tags using the following syntax:

xmlns[:prefix]="someNamespaceHere"

The colon and prefix are optional.  If the namespace is defined without specifying a prefix (e.g., xmlns = "http://www.someName.com"), it applies to the element for which it's defined and children of that element (assuming they don't have namespace prefixes or their own xmlns attribute defined in the same way).  If the namespace is defined with a prefix (e.g., xmlns:ns = "http://www.someOtherName.com"), XML nodes can be associated with that namespace by using its defined prefix and a colon (e.g., an element labelled ns:Element1 would be associated with the namespace "http://www.someOtherName.com").

Let's return to our example.  Look again at the GetHolidayDateResult element in the response above.  Its namespace ("http://www.27seconds.com/Holidays/") is specified as an attribute of its parent element, GetHolidayDateResponse.  Cross-checking this with the soapUI-generated declarations, we can see this namespace was declared for our XPath expression using the prefix "ns1".  We can now write our XPath expression using the correct namespace prefix.  Type the following in the XPath Match Configuration dialog below the namespace declarations:

//ns1:GetHolidayDateResult

The leading double-slashes signify that we want to find an instance of the GetHolidayDateResult element wherever it occurs in our XML tree.  Click on the "Select from current" button below the XPath Expression panel to evaluate the expression-- it should return the value "2013-01-01T00:00:00".  Save the assertion, giving it a meaningful name (so you know at a glance exactly what's passing or failing); in subsequent test runs, the assertion will be re-evaluated and passed or failed accordingly.

As I mentioned at the beginning of this post, namespaces can get a little tricky.  While soapUI will help you out by automatically generating namespace declarations, you're on your own when it comes to using those namespaces correctly in your XPath expression-- if you enter an incorrect namespace prefix (or forget to use one where one is needed), you'll simply get a message that a match can't be found.  In fact, if you see this behavior and you can't find any obvious errors (e.g., typos), a namespace prefix is frequently the culprit and might be the first thing you'll want to double-check.  Note also that soapUI's auto-declared prefix for a given namespace may not necessarily match the prefix defined in the response XML; in this case, you want to use soapUI's declared prefix.

Obviously, this is a very simple example; in the next post (which I promise won't be six weeks from now) we'll look at some more complex expressions.

7 comments:

  1. The sample project was updated today (1/4/2014) to add some parameters to a few of the test requests; if you downloaded the sample project earlier and encountered any issues with incomplete requests, you may want to re-download it again using the new link.

    ReplyDelete
  2. Hey Mike, I have an issue to write XPATH when response contains CDATA tag, Here is WSDL
    www.webservicex.net/uklocation.asmx?WSDL

    XPATH:
    //ns1:GetUKLocationByCountyResponse[1]/ns1:GetUKLocationByCountyResult[1]/ns1:Table[1]/ns1:County/text()

    ReplyDelete
    Replies
    1. Sorry, I haven't had a chance to look at this (I do intend to); don't know if you've found this post yet or if it might be helpful:

      http://testautomationnoob.blogspot.com/2013/10/beginning-soapui-scripting-6-xmlholder.html

      Delete
    2. Yes, having looked at this now, I'd recommend checking out the post indicated above. Once you get to the GetUKLocationByCountyResult element, you're looking at CDATA as its content. What you have to do is pull out the CDATA independently, then use another XPath expression on the CDATA to get to the County element you're looking for.

      If you're trying to get the County as part of a property transfer, you can just set up two transfers: the first acts on the response and gets the CDATA (//ns1:GetUKLocationByCountyResult) and assigns it to its own property. The second transfer acts on the property to which you just assigned the CDATA block, and extracts the County element's value from the CDATA (e.g., //Table[1]/County).

      If you're trying to look at the County as part of an assertion, I believe you'll have to use Groovy Script, but you're basically doing the same thing using XMLHolders: take the response and assign it to an XMLHolder, then use an XPath expression to get the CDATA. Take that CDATA and assign it to a second XMLHolder, then use XPath again to get the County element value. It's not as hard as it may sound-- again, take a look at the post above for an explanation of the XmlHolder class.

      Hope that helps...

      Delete
  3. Hi Mike,
    Thanks for your awesome tutorial. I followed your tutorial and put the following 3 lines in the "XPath Expression" textbox in "XPath Match Configuration" window. Then I clicked "Select from current" button, but I got an error saying "Missing content to select from." What did I do wrong?

    declare namespace soap='http://schemas.xmlsoap.org/soap/envelope/';
    declare namespace ns1='http://www.27seconds.com/Holidays/';
    //ns1:GetHolidayDateResult

    ReplyDelete
    Replies
    1. You may have to run through the requests (run the suite) at least once. The XPath is trying to evaluate against response data which won't be there until after the first time the request is run in your SoapUI session.

      Delete
  4. Hi Mike,
    I have a doubt.Instead of selecting SELECT FROM CURRENT in EXPECTED RESULT,for every value the expected result wants to be changed automatically.Need your help.

    ReplyDelete

Please be respectful of others (myself included!) when posting comments. Unfortunately, I may not be able to address (or even read) all comments immediately, and I reserve the right to remove comments periodically to keep clutter to a minimum ("clean" posts that aren't disrespectful or off-topic should stay on the site for at least 30 days to give others a chance to read them). If you're looking for a solution to a particular issue, you're free to post your question here, but you may have better luck posting your question on the main forum belonging to your tool's home site (links to these are available on the navigation bar on the right).