Count() and Numeric Functions
count(//Holidays)
There are several numeric functions similar to the count() function that perform operations on expressions that return multiple matches. For example, Holidays elements returned by the GetHolidaysAvailable test request have a numeric rowOrder attribute. We can use the following functions to find their minimum, maximum, average, and sum:
min(//Holidays/@msdata:rowOrder)
(returns 0.0)max(//Holidays/@msdata:rowOrder)
(returns 23.0)avg(//Holidays/@msdata:rowOrder)
(returns 11.5)sum(//Holidays/@msdata:rowOrder)
(returns 276.0)String Functions
contains(//Holidays[6]/Name , 'Patrick')
(returns true)The XPath expression
//Holidays[6]/Name
evaluates to "St. Patrick's Day" (the Name of the sixth Holidays element in the response). As the first argument to the contains() function, the string "St. Patrick's Day" is checked to see if it contains the second argument, the string "Patrick". Consequently, the function returns the Boolean true.Some example expressions using the starts-with() and ends-with() functions:
starts-with(//Holidays[8]/Key , 'GOOD_')
(returns true)ends-with(//Holidays[13]/Name , 'Mayo')
(returns true)Date Functions
month-from-dateTime(//Holidays[Name = 'Cinco de Mayo']/Date)
(returns 5)day-from-dateTime(//Holidays[Name = 'Cinco de Mayo']/Date)
(returns 5)There are corresponding functions to retrieve the year, hours, minutes, or seconds from a full date and time value-- year-from-dateTime(), minutes-from-dateTime(), etc.-- plus functions to do the same with date- or time-only values-- year-from-date(), month-from-date(), hours-from-time(), seconds-from-time(), and so on.
Not() Function
For example, the GetHolidaysForYear test request returns holidays for a specified year. One check we might want to do is to confirm that all of the dates are indeed within the specified year. We know that no matter how many holidays are returned, zero holidays should be returned that don't fall in the specified year (we'll use 2013 in this case). Here's a function that uses count(), not(), starts-with(), and logical operators to confirm that at least one holiday is returned (to make sure we're not just counting zero elements to begin with-- which would indicate something else is going wrong) and none of them fall outside 2013:
count(//Holidays) > 0 and count(//Holidays[not(starts-with(Date , '2013'))]) = 0
(returns true)The first half of the assertion (before the and operator) should be pretty self-explanatory, but let's analyze the second part from the inside out, beginning with the starts-with() function, which checks the value of the Date element (as a string) to see if it begins with "2013". The result of the starts-with function, a Boolean value, is negated by the not() function. All of this is used as a predicate (within brackets) to target certain Holidays elements-- i.e., return only those Holidays elements with Date child elements that don't start with "2013". Finally, matches found for that expression are counted by the count() function. As we expected, the result of the count() function is 0-- none of our Holidays elements have dates that don't fall in 2013.
The example illustrates one of the potential pitfalls when working with longer, more complex expressions and multiple functions-- navigating the tangle of brackets and parentheses. If you receive an "Invalid XPath expression" error trying to evaluate a complex expression like this, be sure to double-check the order and count (always in opening and closing pairs) of parentheses and brackets. Also, if you get stuck building an expression with nested functions, you may find it helpful to back up and try working from the inside out, if possible, checking to make sure each part works by itself before putting all the pieces together.
In the next post, we'll look at using XPath with property transfers in soapUI.