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.

Beginning soapUI Scripting: "Groovifying" Your Scripts

In this post I'm going to dive into some features specific to Groovy; mainly, alternative ways to work with properties and Groovy closures.  You're likely to come across these as you look at more scripting examples on the web, and although you don't have to use these concepts, once you feel comfortable with them you may choose to incorporate them into your own scripts to make them more compact and efficient.

Working with Properties with Groovy

Groovy provides a couple of alternative ways to work with properties.  Take the following code, for example, which illustrates three different ways to set and retrieve the value of a user-created property:

//Setting a user-defined property the "traditional" way:
context.setProperty("myVar1", "Value1")
//Setting a property using map syntax-- treating property names 

//and values as key-value pairs:
context["myVar2"] = "Value2"
//Setting a property using simple dot notation:
context.myVar3 = "Value3"

//Get the "traditional" way:
log.info("Value of myVar2 = " + context.getProperty("myVar2"))
//Get using map syntax:
log.info("Value of myVar3 = " + context["myVar3"])
//Get using simple dot notation:
log.info("Value of myVar1 = $context.myVar1")

This produces the following output:



You can see all three methods for setting and getting properties are functionally equivalent to each other.

These aren't restricted to user-defined properties; Groovy lets you use the same shortcuts to work with objects' intrinsic properties-- the properties that are class members, typically accessed via their own "getter" and "setter" (sometimes called accessor and mutator) methods.  For example, the WsdlTestCaseRunner class has a method called getResultCount(), which returns the number of step results returned in a test case run.  You can access the same data using simple dot notation and map syntax:

//"Traditional" method to access result count
log.info(testRunner.getResultCount())
//Alternative ways to get the result count
log.info(testRunner.resultCount)
log.info(testRunner["resultCount"])

Closures

You can implement your own lightweight user-defined functions via closures in Groovy.  Closures are defined much like typical variables, except you're assigning code instead of standard data.  The code being assigned to the closure is wrapped in braces:

def logTCProp = {log.info("$it = " + testCase[it])}

Here we're defining a closure called logTCProp; its purpose is to take a test case property name (a string) and write the name and its value to the script log (note this closure should be run in a test case setup or tear down script, where the testCase variable is available).  The keyword it serves as a placeholder for the argument passed in when the closure is called:

logTCProp('name')

This calls the logTCProp closure, passing in the string 'name' as its argument.  When the closure is evaluated, 'name' replaces the it keyword; effectively, it becomes equivalent to the following:

log.info("name = " + testCase['name'])

The resulting output (for a test case named "ClosureExamples"):



The real power of closures is in their ability to be passed as arguments to certain methods (typically related to collections of data-- lists, for example).  Here's an example using the each() method with a Groovy list of strings, using the logTCProp closure we defined previously as its argument:

def propList = ['name' , 'testSuite' , 'testStepCount']
propList.each(logTCProp)


First we define propList as a list of test case property names, then each() is called as a method of the list, with the closure passed in as its argument.  Basically, each() ends up functioning like a for loop: the specified closure (the argument) is executed using each member of the calling list in turn as its argument.  Compare it to:

for(prop in propList){
logTCProp(prop)
}


The resulting output (from the each() method and the for loop):



Another method that can take a closure as its argument is collect().  The collect() method can be used with closures that produce some output value-- as with each(), collect() executes a specified closure against each member of a list, but collect() puts the values output by the closure together into a new list.  Here's an example that gets a test case's step results and puts their statuses into a list:

def returnStatus = {it.status}
def resList = testRunner.results
def statusList = resList.collect(returnStatus)
log.info(statusList.toString())


Example output:



You can save yourself even more typing by taking advantage of anonymous closures-- there's no need to name and define the closure separately from the method calling the closure; instead you can simply place the closure's code (braces and all) after the name of the method with which you'd like to use it. Here's the example I just gave rewritten using an anonymous closure:

def resList = testRunner.results
def statusList = resList.collect{it.status}
log.info(statusList.toString())

Note that returnStatus is no longer defined anywhere-- instead, its bit of code ({it.status}) is placed it after the call to the collect() method.  Also note that there are no parentheses used with the method call (as we do when using a named closure)-- it's as if the braces around the code take their place.

For more on working with Groovy, check out its official site (I recommend the "Beginner's Tutorial" section of the Getting Started Guide as a starting point).

Quick Notes: Thanks and the def Keyword

With the Test Automation Noob blog rolling past 17000 page views, I wanted to thank everybody who has asked questions, taken a few moments to send some feedback regarding posts, or recommended the blog to others; hearing from readers has been very gratifying.  I also apologize for the sporadic frequency of postings:  between work, parenthood (is that redundant?), and other pursuits it's sometimes difficult to carve out time to write new posts on a consistent basis.

If you've looked beyond this blog for soapUI scripting samples, you may have noticed some differences between the scripts here and those posted elsewhere.  Some of these are due to differences between Groovy and Java-- frankly, my scripts tend to be "Java-centric" and don't take advantage of many of Groovy's shortcuts; I think some of the Groovy constructs are a little less intuitive and harder to grasp for somebody starting out in scripting, and I came to soapUI with experience in Java, so I never really dove into the nuts and bolts of Groovy.  I hope to write a post in the not-too-distant future to bridge some of those gaps and illustrate some of the different ways of doing things in Groovy that can help make your scripts more compact.  However, there is one Groovy keyword I haven't used thus far, but intend to use in the future: the def keyword, used when declaring a new variable in a script.  If you've already started implementing scripts without def, don't panic!  It's optional; the scripts I've written so far should be working fine without it, and omitting it saves a little bit of typing.  If you only intend to write simple scripts in soapUI for the odd situation where the standard interface isn't quite sufficient, you can probably get away with leaving out the def keyword.  However, moving forward I intend to use the keyword in scripts and you may see some of the past scripts and posts re-worked to include it (look for an update to the first scripting post on variables for more detail on using the def keyword).  I have two primary reasons for doing this: first, as I mentioned before, def is ubiquitous in scripting examples elsewhere, including soapUI's own site, so including it here provides some consistency.  Secondly, after reading over discussions on the topic and weighing different arguments, I think it's just better practice, particularly if you think you may end up working with Groovy outside of soapUI someday-- getting into the habit of using it now when it may not matter could help avoid headaches later on when it does.

Beginning soapUI Scripting 7: More with the XmlHolder Class

You can download a project containing examples using the XmlHolder class (including the scripts covered in this post) here.  While I discuss XPath expressions here, a detailed explanation of how they work is beyond the scope of this post-- if you're unfamiliar with XPath, I highly recommend this tutorial at www.w3schools.com.

In my last post I introduced the XmlHolder class and demonstrated how it can be used with XPath expressions in your scripts.  In this post, I'll continue the discussion of the XmlHolder class with a look at some of the other assertion scripts included in the sample project I provided.

Let's actually start with the script assertion for the GetAtomicWeight test request (comments are removed in script excerpts for the sake of brevity); this one represents a basic case:

import com.eviware.soapui.support.XmlHolder

respXmlHolder = new XmlHolder(messageExchange.getResponseContentAsXml())
respXmlHolder.declareNamespace("ns1","http://www.webserviceX.NET")
CDATAHolder = new XmlHolder(respXmlHolder.getNodeValue("//ns1:GetAtomicWeightResult"))
atomWeight = CDATAHolder.getNodeValue("//AtomicWeight")
assert atomWeight == "1.00797"

If you've read my last post, this should all be familiar.  The import statement makes the XmlHolder class available in the script, and the next three lines pluck the CDATA section out of the response, putting it into the CDATAHolder XmlHolder object.  In line 4 of the main body of the script, the getNodeValue() method returns the value of the AtomicWeight element in the CDATA section, assigning it to the variable atomWeight, and the final line asserts that atomWeight matches the expected value of "1.00797".

Let's move on to a more interesting example in the script assertion for the GetAtoms test case:

import com.eviware.soapui.support.XmlHolder

respXmlHolder = new XmlHolder(messageExchange.getResponseContentAsXml())
respXmlHolder.declareNamespace("ns1","http://www.webserviceX.NET")
dataTableXmlHolder = new XmlHolder(respXmlHolder.getNodeValue("//ns1:GetAtomsResult"))
elementValues = dataTableXmlHolder.getNodeValues("/NewDataSet//Table/ElementName")
for (i in elementValues) {
 log.info(i)
}
countVal = dataTableXmlHolder.getNodeValue("count(/NewDataSet//Table)") 
assert Integer.parseInt(countVal) == 112
countAdamantium = dataTableXmlHolder.getNodeValue("count(//Table[ElementName='Adamantium'])")
assert Integer.parseInt(countAdamantium) == 0

Again, the first few lines (up to the creation of the elementValues variable) retrieve the CDATA string in our response XML and put it into an XmlHolder called dataTableXmlHolder.  The next lines illustrate a new method of the XmlHolder class, getNodeValues():

elementValues = dataTableXmlHolder.getNodeValues("/NewDataSet//Table/ElementName")
for (i in elementValues) {
 log.info(i)
}

The getNodeValues() method finds all matches in the XML it "holds" for a given XPath expression, putting their values into a list.  In this particular example, the GetAtoms operation returns all the element names in the periodic table (in a CDATA section); the call to getNodeValues() retrieves the names and puts them into a list called elementValues.  A for loop then iterates over the members of the list and prints them to the script log.

countVal = dataTableXmlHolder.getNodeValue("count(/NewDataSet//Table)") 

This is a call to the familiar getNodeValue() method, but in this case instead of returning the value of a single node we're using XPath's count() function to count the number of elements that match the expression "/NewDataSet//Table"; the result is assigned to the variable countVal.

assert Integer.parseInt(countVal) == 112

An assertion confirms the result of the count() function is 112.  The getNodeValue() method returns data as a string; here we're converting that string to a different data type.  The parseInt() method of Java's Integer class takes a string representation of a number as an argument and converts it to an integer.  Note that the method is called against the Integer class itself, not against an object of that class-- compare this to the getNodeValue() method in the previous line, for example, which is called against dataTableXmlHolder, an object of class XmlHolder.  Methods like this that are called against a class instead of an instance of an object are called static methods in Java.

countAdamantium = dataTableXmlHolder.getNodeValue("count(//Table[ElementName='Adamantium'])")
assert Integer.parseInt(countAdamantium) == 0

The final two lines illustrate using the getNodeValue() method with a more complex XPath expression to confirm a specific XML element or value does not exist in our response.  Here the count() function is used to count the number of elements named "Adamantium" in the result set.  Since adamantium isn't a real element, we assert that the count should equal 0.  Once again we use the parseInt() method to convert the result of the getNodeValue() function to an integer for the purposes of our assertion.

The last assertion script we'll look at comes from the GetElementSymbol test case:

import com.eviware.soapui.support.XmlHolder

respXmlHolder = new XmlHolder(messageExchange.getResponseContentAsXml())
respXmlHolder.declareNamespace("ns1","http://www.webserviceX.NET")
CDATAHolder = new XmlHolder(respXmlHolder.getNodeValue("//ns1:GetElementSymbolResult"))
symbolExists = CDATAHolder.getNodeValue("//Symbol = 'H'")
assert Boolean.parseBoolean(symbolExists)

The first few lines are the standard lines to deal with CDATA.  In the final two lines, another type of XPath expression is used with the getNodeValue() method; in this case, the expression is a comparison ("//Symbol = 'H'") that evaluates to true or false.  Again, since the method always returns its result as a string, we can use another static method, the parseBoolean() method of Java's Boolean class, to convert the result to an actual Boolean value to use in our assertion.

Beginning soapUI Scripting 6: The XmlHolder Class and Working with CDATA

You can download a project containing examples using the XmlHolder class (including the script covered in detail in this post) here.  While I discuss XPath expressions here, a detailed explanation of how they work is beyond the scope of this post-- if you're unfamiliar with XPath, I highly recommend this tutorial at www.w3schools.com.

In this post I'll be looking at soapUI's XmlHolder class, which provides methods to work easily with XML data within scripts.  As you'll see, adding it to your toolbox can open up a lot of new and very useful possibilities, including the ability to mimic XPath assertions.  You may be asking yourself why you'd want to do that-- XPath assertions are included as a standard assertion type, after all, and shouldn't require any additional scripting.  But if you've looked at any of my extended tutorials working with the PeriodicTable sample web service, you've actually seen one scenario where the XmlHolder class could come in handy: working with CDATA.

The CDATA Problem

Below is a screenshot from soapUI that shows the response to a test request to the GetAtomicNumber operation of the aforementioned PeriodicTable web service.  The operation takes the name of an element (hydrogen in this case) as an argument and responds with information about certain properties of that element-- including, of course, its atomic number:



A response from the GetAtomicNumber operation

The XML block containing information about hydrogen is returned as a CDATA section-- it's a string that's returned as the value of the GetAtomicNumberResult node.  Consequently, if you try to create a single standard XPath assertion looking for any of the nodes contained within that CDATA section (if you try to assert that the value of the MeltingPoint element is 14, for example) you'll run into problems.

You could just deal with this with a basic Contains assertion, looking for the MeltingPoint node as a string.  Since the Contains assertion looks at the entire response as a string, the issues related to parsing XML with a CDATA section are bypassed.  However, you may have a particular situation where you can't or don't want to use a Contains assertion.

SoapUI's site provides a detailed explanation of how to deal with this scenario here.  In a nutshell, you can use one XPath expression to isolate the CDATA section, effectively stripping off the rest of the XML in which it's embedded.  Once isolated, you can use further XPath expressions on the XML in the CDATA section.

Scripting may not be necessary in some scenarios where you can use two XPath expressions together (in a Property Transfer step, for example, where you can specify multiple transfers).  If you're trying to create an assertion against a test request that uses CDATA, however, some scripting will probably be required-- and the methods provided by the XmlHolder class can make things a whole lot easier.

Sample Script Using the XmlHolder Class

Let's jump right into a short script.  If you've downloaded the sample project, this is the Groovy assertion for the GetAtomicNumber test request test step (comments are excluded here); it tackles the problem I gave above as an example: verifying that the value of the MeltingPoint XML element is 14:

import com.eviware.soapui.support.XmlHolder

respXmlHolder = new XmlHolder(messageExchange.getResponseContentAsXml())
respXmlHolder.declareNamespace("ns1","http://www.webserviceX.NET")
CDATAXml = respXmlHolder.getNodeValue("//ns1:GetAtomicNumberResult")
log.info(CDATAXml)
CDATAXmlHolder = new XmlHolder(CDATAXml)
meltPoint = CDATAXmlHolder.getNodeValue("//MeltingPoint")
log.info("Melting Point = $meltPoint")
assert meltPoint == "14"

Let's look at this script line by line:

import com.eviware.soapui.support.XmlHolder

The XmlHolder class is not included with soapUI's core components; therefore, this line is required to import it from its package, the com.eviware.soapui.support package, for use in the script.

respXmlHolder = new XmlHolder(messageExchange.getResponseContentAsXml())

This line creates a new XmlHolder object and assigns it to the variable respXmlHolder.  The XmlHolder constructor (the method used to create our new XmlHolder object-- the constructor has the same name as the class itself and is preceded by the new keyword) takes a string of XML as an argument.  In this case, the XML we're interested in getting is our response data-- and conveniently, soapUI provides us with the built-in messageExchange variable in script assertions.  We can use this variable's getResponseContentAsXml() method to quickly get our test request's response XML (as a string).  We end up with an XmlHolder object that contains and can act on our response XML.

respXmlHolder.declareNamespace("ns1","http://www.webserviceX.NET")
CDATAXml = respXmlHolder.getNodeValue("//ns1:GetAtomicNumberResult")
log.info(CDATAXml)

In these lines we're using our XmlHolder object (respXmlHolder) to use an XPath expression with the response XML.  The first line declares a namespace to use in our XPath expression, assigning the URI "http://www.webserviceX.NET" to the prefix "ns1".  In the next line, the XPath expression itself is passed as an argument (a string) to the getNodeValue() method of the XmlHolder object; note how the XPath expression uses the namespace prefix we just defined in the previous line: //ns1:GetAtomicNumberResult.  This XPath expression returns the value of the GetAtomicNumberResult element in the response XML, which is the CDATA section.  The result of the evaluation is assigned to the variable CDATAXml and (for informational purposes only) the contents of CDATAXml are written to the script log-- you should see a string of XML in the log starting (and ending) with a "NewDataSet" tag:



At this point we've basically isolated the CDATA section of our original XML response; now we can use that data with XPath expressions.

CDATAXmlHolder = new XmlHolder(CDATAXml)
meltPoint = CDATAXmlHolder.getNodeValue("//MeltingPoint")
log.info("Melting Point = $meltPoint")

These lines are similar to preceding lines, repeating many of the same actions.  In the first line we create another XmlHolder object, this time using the variable CDATAXml as the argument to its constructor, and assign it to variable CDATAXmlHolder.  In the second line, we use the getNodeValue() method to evaluate the XPath expression //MeltingPoint.  The result of the evaluation is assigned to the variable meltPoint and written to the script log:



Finally, we have our assertion:

assert meltPoint == "14"

The assertion checks to make sure the value we previously retrieved from the MeltingPoint element matches its expected value.  There's one crucial thing to note here: the getNodeValue() method returns a string value.  This is taken into account in our assertion, which checks that the value of meltPoint matches "14"-- the string representation of the number 14-- instead of a pure numeric representation.

One final note: I see a NullPointerException when the getNodeValue() method is used in an assert statement and the assertion fails (there's no error if the assertion passes).  I'm not sure if this is an as yet unidentified issue with my script or a minor bug in soapUI (I'm using version 4.5.1).  Passing the result of the getNodeValue() method to a variable and then using that variable with the assert statement (as I did in the example above) seems to avoid the error.

I'll look at some key points in the remaining assertion scripts in the example project in my next post.

soapUI: Scripting Objects - MessageExchange Interface

This is a continuation of a series of posts attempting to review some of the objects (and methods) you're likely to encounter when you start scripting in soapUI.  To see the full soapUI javadoc, click here.

Common MessageExchange Methods

SoapUI's MessageExchange interface defines methods for retrieving information related to test requests, including request and response data.  Within a Groovy Script assertion, a built-in variable (messageExchange) provides access to an object of a class that implements the MessageExchange interface.  Several step result classes (including the WsdlTestRequestStepResult class) also implement the MessageExchange interfaces.  Some of the common methods of the interface (available to objects of all classes that implement the interface) include:

getEndpoint() : returns the target endpoint as a String
getRequestContent() : returns the content of the test request
getRequestContentAsXml() : returns the content of the test request (as XML)
getResponseContent() : returns the content of the response to the test request
getResponseContentAsXml() : returns the content of the response to the test request (as XML)
getTimeStamp() : returns the request time stamp as a long number (see below for an example of converting from long to a standard time format)
getTimeTaken() : returns the amount of time taken to process the request (in milliseconds)
- getRequestHeaders() : returns request headers as a StringToStringsMap object
- getResponseHeaders() : returns response headers as a StringToStringsMap object

A map (as implemented in the StringToStringsMap objects returned by the getRequestHeader() and getResponseHeader() methods) is similar to a list in that it's capable of storing multiple data items.  Map "entries" are stored as data pairs: a key and a value.  So while an item in a list is retrieved using its location in the list order (its index), a data value in a map is retrieved using its associated key.  If that explanation still unclear, see the example below using the getResponseHeaders() method, which illustrates retrieving data from a StringToStringsMap object.

Examples Using MessageExchange Methods

These coding examples use the PeriodicTable service I've used in many other posts and reflect a test request call made to its GetAtomicNumber operation.  The first example illustrates some of the methods illustrated above as they might be used in a Groovy Script assertion, called using the script assertion's built-in messageExchange variable:

log.info("MessageExchange class = " + messageExchange.getClass().toString())
log.info("Endpoint = " + messageExchange.getEndpoint())
log.info("Request Content = " + messageExchange.getRequestContent())
log.info("Response Content Xml = " + messageExchange.getResponseContentAsXml())
log.info("Time Taken = " + messageExchange.getTimeTaken() + "ms")
//Using a map: get the map and assign it to variable respHeadersMap
respHeadersMap = messageExchange.getResponseHeaders()
//Retrieve the map's keys and assign them to a list, respHeadersKeys
respHeadersKeys = respHeadersMap.getKeys()
log.info("Response Headers info:")
/*Use a for loop to step through each key in the map and
  use the map's get function to get each key's corresponding
  map data.  The get function takes two arguments: the first
  is the key for which you'd like the corresponding data, the 
  second is a default value if the key can not be found in the map.*/
for(key in respHeadersKeys){
 log.info("   " + key + ": " + respHeadersMap.get(key,"NONE"))
}
//To clarify, here's the Date header retrieved again:
log.info("   Date (again): " + respHeadersMap.get("Date","NONE"))
/*And here's a call to retrieve from a key that doesn't exist;
  the default value of "NONE" is used instead.*/
log.info("   Bogus: " + respHeadersMap.get("Bogus","NONE"))

The script log output looks something like this:



As I mentioned above, you're not restricted to using these methods with the messageExchange variable in script assertions-- you can use them with any class that implements the MessageExchange interface.  Here's another example taken from a Groovy Script step that uses a WsdlTestRequestStep object to invoke some of the methods:

//Get the result for the first step, a test request step
//Assign it to variable tReqResult
tReqResult = testRunner.getResults()[0]
log.info("tReqResult Class = " + tReqResult.getClass().toString())
log.info("Endpoint = " + tReqResult.getEndpoint())
log.info("Request Content Xml = " + tReqResult.getRequestContentAsXml())
log.info("Response Content = " + tReqResult.getResponseContent())
//Assign the value returned by getTimestamp() to variable timeStampAsLong
timeStampAsLong = tReqResult.getTimestamp()
//Use long value stored in timeStampAsLong to create a new Date object, assigned
//to variable timeStampAsTime; this can be used to get a "friendly" date format.
timeStampAsTime = new Date(timeStampAsLong)
log.info("Time Stamp = " + timeStampAsTime)

And the expected output:

Beginning soapUI Scripting 5: Groovy Assertions

So far we've looked at a few scripting "containers" in soapUI-- components where you can easily insert Groovy script to extend or customize functionality-- including Groovy Script test steps and set up and tear down scripts at the test case and test suite levels.  In this post we'll look at Groovy Script assertions, which are useful in situations where soapUI's built-in assertions don't quite fit the bill.

Script assertions can be added to a test request through the standard Add Assertion dialog, launched from the test request editor window:



Test Request Editor (Add Assertion button outlined in red)

Once you select Script Assertion (listed in its own Script category) as the type of assertion you want to add, click the Add button and provide a name for your new assertion to bring up an empty Script Assertion window:



Script Assertion Editor

As with other scripting components, script assertions come with several built-in variables.  We've already seen the log and context variables with Groovy Script steps and set up and tear down scripts, but script assertions also come with the messageExchange variable, which provides quick access to request and response data.

Here's a quick example of some script assertion code based on the periodic table service I've used in other posts (you can download the very, very small project here to play around with the script assertion yourself). It essentially replicates the functionality of a standard Contains assertion, looking for the appropriate string in a response from the service's GetAtomicNumber operation for the element hydrogen:

responseData = messageExchange.getResponseContent() log.info(responseData) assert responseData.contains("<atomicnumber>1</atomicnumber>")
The first line grabs the content of the test request's response (as a string) and assigns it to the variable responseData; it also illustrates the convenience of the built-in messageExchange variable: we're able to access test request data with a single method call (I'll look at some of its other methods in a separate post in the near future). The second line is strictly for informational purposes; it just writes the responseData string to the script log.  The assert keyword is introduced in the second line.  It asserts that the expression following the keyword is true (in this case, we're asserting that the responseData string contains the correct atomic number for hydrogen. If the asserted expression is false, the assert statement (and consequently, the entire script assertion) fails.

The assertion as written should pass, but try changing the substring in the contains method to something else-- changing the atomic number to 2, for example.  You'll see that the assertion is flagged as a failure in the UI just as a standard assertion failure would be flagged:



Script Assertion Failure

Learning Resources: Coursera

A few months ago I wrote a post on several learning resources for Java; this time around I thought I'd write about a single learning resource for... well, quite a lot: Coursera.  I imagine most people are already familiar with it, but for those who aren't, Coursera provides online courses in a variety of subject areas for free.  Founded by two Stanford faculty members, it counts some top-notch institutions among its partners (70+ as of this writing).

Currently, courses are offered for free, but Coursera ultimately intends to generate revenue.  To that end, they've begun implementing some pay services; for example, they recently introduced a special service called Signature Track that (for a premium) awards students who complete courses with certificates securely tied to their identities, making it easier to share them with potential employers, etc. It may be their hope that revenue from these ancillary services will enable them to continue providing the course material for free, but I suppose some sort of fee-based system isn't necessarily out of the question in the future.  All of which is to say: I'd recommend availing yourself of the service as soon as possible.

As with a "real" course, the material is broken up into units (normally weekly, in my experience), so you and your virtual classmates progress through the units together; course-specific online forums facilitate communication between students and teachers.  Lectures are delivered via online videos with short questions embedded throughout to gauge your understanding; you can listen to the lectures at your own pace (within the time frame for each unit, of course).  Quizzes at the end of each unit generally comprise part of your course grade, which may also include graded exercises, projects, and exams.

Given Coursera's format, it should come as no surprise that a large number of technology- and programming-oriented courses are offered.  While there are no courses (that I'm aware of) for learning Java, I would recommend the Learn to Program: The Fundamentals and Learn to Program: Crafting Quality Code courses offered by the University of Toronto.  As their names imply, they're intended to provide an introduction to programming concepts in general, but Python is the language of choice so they end up being pretty good introductions to Python specifically, too (Python is the primary scripting language used by Sikuli, the UI automation tool I've discussed in other posts).

If you're hesitant to jump into the pool and would like to sample a course to see if this learning method is right for you, there are currently two computer science courses available for self study: Introduction to Databases and Computer Science 101, both from Stanford University.  Without due dates and a live online forum (and there's no statement of accomplishment for completing self study courses) it's not quite the same thing, but at the very least you can get a sense of what online lectures and quizzes are like.  The Computer Science 101 course may seem very basic to people who work with computers on a daily basis, but for anybody unfamiliar with programming/scripting, the first three units teach some fundamental concepts including variables, dot notation, and flow control, albeit via a simplified proprietary Javascript-like language developed by the course instructor (Nick Parlante-- who also developed Google's Python Class).  The "image puzzles" in the course are also kind of cool.

soapUI: Data-Driven Testing with Excel (xlsx)

Posts on data-driven testing seem to be fairly popular, so I'm visiting that well one last time.  So far I've covered data-driven testing with csv files and xls files; in this post, we'll look at adapting the DDT script to use an xlsx file as our data source.  If you've read the post on using an xls data source, most of this post will look very familiar-- in fact, the only differences are a few changes in the Apache POI classes used for our objects, so you may want to skip to looking at the script itself, paying particular attention to import statements and classes used.  For readers new to scripting concepts or otherwise having difficulty following along, you may want to check out some of the beginning scripting posts or the post on data-driven testing with a csv file (see the soapUI posts launch page for the relevant links).

To review the basic approach used in this script (and as you learn more about scripting in soapUI and look at other examples, you'll see that this is only one of many possible approaches):

1) Open the xlsx source file and read in a single row, which consists of inputs with some expected response data, and copy the data to test case properties.
2) Execute our test requests, plugging the inputs into test requests and expected results into content assertions via property expansion.
3) The last test step in the test case is a Groovy script step; if the end of the source file hasn't been reached, this step processes the next line in the source file and sends execution back to step 2.
4) The data-driven test case ends when the end of the file is reached, and the second test case (which only executes once) is run.

SoapUI doesn't have a native capability to read Excel files, so we have to use third-party components; in this case we're using the Apache POI API, which you can download from its site here-- you'll probably want the binary distribution (note: as I'm writing this, there seems to be an issue with the download links on the download page-- if you encounter issues, try following the link for "Binary Artifacts" in the "Release Archives" section at the bottom of the download page and get a version from there).  Unzip the downloaded file and drop the main jar file (something like poi-3.9-20121203.jar) into the /bin/ext directory of your soapUI program directory (I'm assuming that you already have soapUI installed).  This is a special directory for external jar files that allows soapUI to easily import their contents (we'll get to the import in a little bit).

The suite and xlsx source file can be downloaded here.  Unzip the contents and import the project xml file into soapUI (File - Import Project).  By default, the script looks for the xlsx file in a directory called C:\PerTableData; if you want to put it anywhere else you'll have to modify a single line in the script.  Also note that the source file intentionally contains some incorrect data to demonstrate how failures would appear (you should see three).

There are a couple of wrinkles in our source file.  First of all, there's a mix of data types: we have integers for the most part, but we have some floats (the boiling points for helium and oxygen, for example) and one number represented as a string (the boiling point of hydrogen).  Additionally, the atomic number for oxygen is represented with a SUM formula.  Generally speaking, your best bet is to work with strings-- property-related "set" and "get" methods in soapUI work with strings-- but this post should illustrate that it's possible to work with numeric data, too.

Let's start with the setup script for the ElementSpecificRequests test case (the data-driven test case).  The script begins with two import statements:

import org.apache.poi.xssf.usermodel.*
import org.apache.poi.ss.usermodel.DataFormatter

These lines are where the components we need are imported from the POI jar file for use in our script.  Those of you familiar with my previous post on using xls files should make note of the use of xssf instead of hssf in our first imported package.

The next few lines set up some of the objects we'll need, including some based on POI classes.  The DataFormatter object (dFormatter) is used to handle the formatting of numeric data when it's converted to text for use with our property setter methods.  The XSSFFormulaEvaluator object (fEval) is used to handle cells with formula data (in this case, the atomic number of oxygen is the only such cell).  The XSSFWorkbook object (srcBook) represents our source xlsx file (you'll have to modify the source file path in the line where this object is created if you put the source file anywhere other than the default location).  Finally, the first sheet is extracted from the workbook object using its getSheetAt() method:

//Create data formatter
dFormatter = new DataFormatter()

//Create a new workbook using POI API
srcBook = new XSSFWorkbook(new FileInputStream(new File("C:\\PerTableData\\TestData-Mix.xlsx")))

//Create formula evaluator to handle formula cells
fEval = new XSSFFormulaEvaluator(srcBook)

//Get first sheet of the workbook (assumes data is on first sheet)
sourceSheet = srcBook.getSheetAt(0)

On to the mechanics of reading in data from the source file, starting with establishing our counter variable-- note that it's set as a context property with the built-in context variable so we can share its value across test components:

//Sets row counter to 0 (first row)-- if your sheet has headers, you can set this to 1
context.rowCounter = 0

Next we get the first row of data in the spreadsheet using the getRow() method, then step through the cells in that row, copying their contents to the corresponding test case properties using the setPropertyValue() method. Note the use of our DataFormatter object (dFormatter) with its formatCellValue() method-- this handles some potential problems with formatting numeric values when they're converted to strings.  Additionally, the XSSFFormulaEvaluator object (fEval) is used as an argument with the formatCellValue() method to handle the case where a cell has formula data.  Some of you may recognize this as overkill given our data source-- we know some of the columns (like element name) contain text data exclusively, but using these objects and methods allows for some flexibility moving forward.

//Read in the contents of the first row
sourceRow = sourceSheet.getRow(0)

//Step through cells in the row and populate property values-- note the extra work for numbers
elNameCell = sourceRow.getCell(0)
testCase.setPropertyValue("ElName",dFormatter.formatCellValue(elNameCell,fEval))

atNumCell = sourceRow.getCell(1)
testCase.setPropertyValue("AtNum",dFormatter.formatCellValue(atNumCell,fEval))

symbolCell = sourceRow.getCell(2)
testCase.setPropertyValue("Symbol",dFormatter.formatCellValue(symbolCell,fEval))

atWtCell = sourceRow.getCell(3)
testCase.setPropertyValue("AtWeight",dFormatter.formatCellValue(atWtCell,fEval))

boilCell = sourceRow.getCell(4)
testCase.setPropertyValue("BoilPoint",dFormatter.formatCellValue(boilCell,fEval))

The next few lines are optional-- these rename test steps for readability (so you can more easily identify which property values are being used in the test request):

//Rename request test steps for readability in the log; append the element name to the test step names
testCase.getTestStepAt(0).setName("GetAtomicNumber-" + testCase.getPropertyValue("AtNum"))
testCase.getTestStepAt(1).setName("GetAtomicWeight-" + testCase.getPropertyValue("AtWeight"))
testCase.getTestStepAt(2).setName("GetElementySymbol-" + testCase.getPropertyValue("Symbol"))

Finally, we add a reference to the sheet as a context property:

context.srcWkSheet = sourceSheet

With the data from our spreadsheet plugged into test case properties, the test requests are run, after which we get to the ReadNextLine Groovy script step.  As you might expect, this script is responsible for processing the next line of data and controlling execution accordingly.

As in the setup script above, the first few lines import the necessary contents of the POI package and create required objects.  Note the use of the srcWkSheet context property (referencing a worksheet object) we created at the end of our setup script and the call to its getWorkbook() method.

import org.apache.poi.xssf.usermodel.*
import org.apache.poi.ss.usermodel.DataFormatter

cellDataFormatter = new XSSFDataFormatter()

//Create formula evaluator
fEval = new XSSFFormulaEvaluator(context.srcWkSheet.getWorkbook())

The next line increments our counter variable, stored in test case context:

//Increment the rowcounter then read in the next row of items
context.rowCounter++;

The code to actually read in row data should look familiar-- it's nearly identical to code from the setup script, except it's wrapped in an if statement that checks for the end of the file (using the getLastRowNum() method of the POI XSSFSheet class). The code in the if block is only executed when the end of the file hasn't been reached; the last line sends execution back to the first test request step and repeats the loop, this time using the newly copied property values.

if(context.rowCounter<=context.srcWkSheet.getLastRowNum()){
 curTC = testRunner.testCase
 sourceRow = context.srcWkSheet.getRow(context.rowCounter)//Get a spreadsheet row
 
 //Step through cells in the row and populate property data 
 elNameCell = sourceRow.getCell(0)
 curTC.setPropertyValue("ElName",cellDataFormatter.formatCellValue(elNameCell,fEval))

 atNumCell = sourceRow.getCell(1)
 curTC.setPropertyValue("AtNum",cellDataFormatter.formatCellValue(atNumCell,fEval))

 symbolCell = sourceRow.getCell(2)
 curTC.setPropertyValue("Symbol",cellDataFormatter.formatCellValue(symbolCell,fEval))

 atWtCell = sourceRow.getCell(3)
 curTC.setPropertyValue("AtWeight",cellDataFormatter.formatCellValue(atWtCell,fEval))

 boilCell = sourceRow.getCell(4)
 curTC.setPropertyValue("BoilPoint",cellDataFormatter.formatCellValue(boilCell,fEval))

 //Rename test cases for readability in the TestSuite log
 curTC.getTestStepAt(0).setName("GetAtomicNumber-" + curTC.getPropertyValue("AtNum"))
 curTC.getTestStepAt(1).setName("GetAtomicWeight-" + curTC.getPropertyValue("AtWeight"))
 curTC.getTestStepAt(2).setName("GetElementSymbol-" + curTC.getPropertyValue("Symbol"))

 //Go back to first test request with newly copied properties
 testRunner.gotoStep(0)
}

This general technique should work for most xlsx data sources, but you may need to modify some of the code depending on your particular test data and its formatting standards. If you want to explore the Apache POI API more thoroughly, its documentation can be found here.

soapUI: A New "Launch Page" for soapUI Posts

I've written quite a few posts about soapUI so far (and intend to write quite a few more), so I've added a new "launch page" to the side navigation bar to make it easier to find and access material.  It's sort of like a virtual table of contents, grouping posts by general topic and briefly summarizing each one.  I'll try to keep the page up to date as I add more soapUI-related information.

Beginning soapUI Scripting 4: Lists, for, and if

Many of the methods you'll encounter in soapUI return lists, objects that can contain multiple data items.  In this post we'll cover some of the techniques and methods for working with lists, the for statement-- which is frequently used with lists, and the if statement, used for basic control flow.  For this post we'll actually use these concepts (and some of the others previously covered) with HTTP test requests, illustrating their practical application in soapUI.

Working with Lists in Groovy

In Groovy, you can create a list using the following syntax:

def myList = ["One", "Two", "Three", "Four"]

This creates a list of four strings.  Note the use of brackets ([ and ]) to enclose the items in the list; each item is also separated by a comma.  You can retrieve individual items in the list like this:

myList[0]

This would return the first item in the list-- "One" in this case.  The format is listName[x], where x is the zero-based position, or index, of the item in the list.  By zero-based, I mean we start counting at zero when determining an item's index: the first item in the list is at index 0, the second is at index 1, the third is at index 2, etc.

Since lists are objects, they have their own methods; here's some code illustrating a few of them:

def myList = ["One", "Two", "Three", "Four"]
log.info("List contents: $myList")
//size()-- returns number of items in the list
log.info("Size is " + myList.size())
log.info("Element one is " + myList[0])
//indexOf()-- returns the index of a given item in the list
log.info("Index of 'Four' is " + myList.indexOf("Four"))
//add()-- adds an item to the list
myList.add("Five")
log.info("After adding 'Five', size is " + myList.size())
log.info("Element five is " + myList[4])
//remove()-- removes an item from the list
myList.remove("Two")
log.info("After removing 'Two', size is " + myList.size())
log.info("List contents now: $myList")

The output from this script:


A SoapUI Project Using Lists

Now let's take a look at our example project; you can download a zipped version of it here. Download it, unzip it, and import it into soapUI.  The test suite consists of a single test case with four HTTP test requests; the service we're testing takes a zip code and returns the U.S. state where the zip code is located.

The SetZipList Groovy Script step establishes a list of zip codes to use with the HTTP requests and sets it as a context property (note that using a list here is done for illustrative purposes; in truth it would probably be more practical to just hard code the zip codes in each test step):

context.zipList = ["19106","20500","10118","57751"]

The members of the list are plugged into each HTTP request using a special form of property expansion.  Here's a screenshot of the first HTTP request step:


Note the value for the ZipCode parameter of our request-- this is a property expansion expression incorporating Groovy Script.  The "$" and enclosing braces are standard property expansion syntax; however, the "=" signifies that what follows within the braces is Groovy Script.  context.zipList[0] returns the first item in the list we created in the first test step ("19106"), using it as our ZipCode parameter in the request.  Each HTTP request step is set up the same way, with each retrieving a different item in the list (the second request gets the second item, the third the third item, etc.).

The following script is in the test case tear down:

def resultsList = testRunner.getResults()

for(res in resultsList){
    if(res.getTestStep().getClass() == com.eviware.soapui.impl.wsdl.teststeps.HttpTestRequestStep){
        def tStep = res.getTestStep()
        log.info(" Step Name = " + tStep.getName())
        log.info("    URL = " + res.getProperty("URL"))
        log.info("    Status = $res.status")
        log.info("    Time Taken = $res.timeTaken ms")
    }
}

This script retrieves select information from our test results and writes them to the log.  The first line uses the getResults() method of the test case's built-in testRunner object to retrieve the results of the test case's test steps, returned in a list.

Using for and if

Frequently when dealing with a list, you'll want to iterate through it and perform some action or set of actions on each individual item.  A for statement is one way to do this.  The basic syntax:

for (varName in list) {
     Set of actions to perform...
}

This takes each item in list list and assigns it to the variable varName, then performs the actions contained in the braces ({ and }).  The code is repeated for every item in the list.  So in our script above, the first result in resultsList is assigned to variable res and the code in the brackets is executed.  If there's another item in the list, that item then gets assigned to res and the code in the brackets is repeated, and so on, until all the items in the list have been processed.

You should recognize most of the code within the brackets as method calls, but the line starting with if may be unfamiliar.  There's a problem in our for loop-- it iterates over every item in our list of results, including results from the first test step, the Groovy Script test step.  Consequently, not all of the method calls we attempt to make are valid-- the getProperty() call, for example, would fail when we tried to call it on the results from the first test step.

The if statement allows us to run or not run code based on certain criteria.  The basic syntax for the if statement:

if (test expression that evaluates to true or false){
     Code to execute if test expression is true
}[else{
     Code to execute if test expression is false
}]

The else clause is optional, and in fact there's no else clause used in our example.  The if statement there checks to see if our results were generated by an HTTP test request step-- if they were (the expression evaluates to true), then the code in the brackets is safe to execute; if they weren't, the code in the brackets is skipped.  Note the operator used to check for equality-- == and not = as you might expect.  Other basic comparison operators include > (greater than), < (less than), >= (greater than or equal to), <= (less than or equal to), and != (not equal to).

Also note the indentation used in the script; with each block an extra level of indentation is used.  While not strictly required, this is considered good practice for readability-- you can easily see where the for block and if block begin and end.

Finally, here's the output in the script log produced by the tear down script:

Beginning soapUI Scripting 3: Setup and Tear Down, Context Properties

You can download a "shell" project to import into soapUI and use for scripting here.

In all the scripting we've done up until this point, we've worked exclusively with Groovy Script test steps.  It's time to introduce some more of soapUI's scripting components: setup and tear down scripts. These aren't test steps per se; rather, they're special scripting components available for test cases and test suites through their editors.  The screenshot below shows the buttons to access a test suite's setup and tear down scripts highlighted in blue (the buttons are in pretty much the same place in the test case editor):


At the test suite level, setup scripts are run prior to any of the suite's test cases and tear down scripts are run after the last test case is completed.  At the test case level, setup scripts are run prior to any of the test case's test steps and tear down scripts are run after its last test step is completed.  The resulting order (assuming each component exists): 1) suite setup, 2) test case setup, 3) test case test steps, 4) test case tear down (steps 2 through 4 are repeated for any additional test cases), 5) suite tear down.

Now that we have different scripting components, let's consider the problem of sharing variables between them.  You might want to do this for the purposes of looping (for a counter variable, for example), when working with a file whose contents need to be re-used among components, etc.  By default, variables created in one component aren't available in others.  For example, put the following code into a test case setup script:

def myVar = 1
log.info("In test case setup: myVar = $myVar")


And this in one of the test case's test step:

log.info("In test step: myVar = $myVar")

This code creates a variable called myVar in the test case setup script and prints out a message with its value (for more details about how this line works, see the previous post on strings).  The intention with the test step is to print out the same value using the same variable.  If you try to run this test case, though, it fails.  The setup script message appears in the script log as expected, so the variable seems to be created successfully:


The test step message never gets written to the script log, however.  If you look at the error log, you'll see a message that looks something like this:

Mon May 13 23:11:24 EDT 2013:ERROR:groovy.lang.MissingPropertyException: No such property: myVar for class: Script1

Basically, our test step doesn't recognize myVar.  In the script's current form, myVar only exists in the test case setup script, so it's unavailable to any other component-- you'll encounter an error even if you try to access it from the test case teardown script.

This is a situation where the context variable comes into play.  Like the log and testSuite or testCase variables, context is a built-in variable automatically made available by soapUI; it's indicated in the messages above script windows-- e.g., the "Setup Script is invoked..." message in the test suite editor screenshot above.  You can add your own user-defined variables as properties of context objects (for those of you familiar with user-defined test case and test suite properties, context properties are not the same thing).  This can be done explicitly via the setProperty() method, but you can also treat your variable as a property using dot notation.  Hopefully an example might clarify.  If you put this code in a test case setup script (remember, the lines starting with "//" are comments for clarification-- nothing's executed by the Groovy interpreter):

//Creating a context property via setProperty() method (the long way)
context.setProperty("myString", "Hello World!")
//Creating a context property via direct dot notation (the short way)
context.myString2 = "Hello World (again)!"

And put this into a Groovy Script test step within that test case:

//Getting a context property via getProperty() method
log.info(context.getProperty("myString"))
//Getting a context property using direct dot notation
log.info(context.myString2)

You'll get the following log output:



A few things to note about this example: the context properties are available between components at different levels, created at the test case level but accessible at the test step level.   Also, context.setProperty("myString", "Hello World!") and context.myString = "Hello World!" are functionally equivalent, as are context.getProperty("myString") and context.myString.

The context variable at the test case and test step levels seems to reference the same object; the context variable at the test suite level references a different object, but context properties set in the test suite setup script are still subsequently available at the test case and test step levels.  For example, you could move the test case setup code above to the test suite setup script and you'd still get the same log output.  However, note that while context properties appear to propagate downward (from test suite to test case and test step), they don't propagate up-- context properties created in a test case or test step will not be available with the context variable in the test suite tear down script.

Beginning soapUI Scripting 2: Strings

You can download a "shell" project to import into soapUI and use for scripting here.

Like my first post on scripting, the material in this post is not specific to soapUI, but strings are used so extensively I thought they were worth a brief explanation for beginners who may not be familiar with them.  Put simply, strings are text data, or a sequence of characters; "Hello World!" is an example of a string. The syntax to create a string variable is similar to the syntax we saw in my first post when creating an integer variable:

def myVar = 3
def myString = "Hello World!"


These lines assign the value 3 to the variable myVar and the string "Hello World!" to the variable myString.  Note the use of quotes, one key difference with strings-- those demarcate our text, but are not included with the actual data.  As with the integer variable myVar, once the myString variable has been created, we can reference its value in subsequent lines of our script using its name:

log.info(myVar)
log.info(myString)


This code prints out the values of our variables to the log:



As promised, the enclosing quotes used when the string was created are not included when the string is printed.

Strings can be enclosed in either single quotes or double quotes in Groovy (but not in "pure" Java-- in Java, strings are always enclosed in double quotes).  You can use a mix of both when you want to include one or the other in your string.  If you want to include a single quote as part of your string, enclose the string in double quotes, and vice versa; for example:

def stringOne = "Don't panic."
def stringTwo = '"Look, I have double quotes!"'
log.info(stringOne)
log.info(stringTwo)


This produces:



You can also perform variable substitutions in Groovy; variable names preceded by the "$" character that are enclosed in strings are replaced with their underlying values.  For example:

def myVar = 3
def myString = "The value of myVar is $myVar"
log.info(myString)


The resulting output:



One thing to remember: variable substitution only works with strings enclosed in double quotes-- in a  string enclosed in single quotes, $myVar would be interpreted as a literal (simply printed out "as is" as  "$myVar").

Finally, while strings may behave in some ways like Java's so-called primitives (the basic data types that include integers), they are in fact objects of class String.  Consequently, they have quite a few methods at their disposal that can be used to parse and alter their contents.  There are far too many methods to cover all of them here; see the Java API entry for the String object to see the full list.  Here's some code that demonstrates a few of them, however (the lines that start with "//" in the script are comments-- they're only there for informational purposes):
def stringOne = "   One, two, three    "
log.info("stringOne = '$stringOne'")
//Trim method-- removes leading and trailing whitespace from a string
def stringTrimmed = stringOne.trim()
log.info("Trimmed string = '$stringTrimmed'")
//Contains method-- checks if one string is contained in another
log.info("String 'two' in '$stringTrimmed'?")
log.info(stringOne.contains("two"))
//Length method-- returns the number of characters in the string
log.info("Length of '$stringTrimmed':")
log.info(stringTrimmed.length())
//EndsWith method-- checks if string ends with certain characters
log.info("'$stringTrimmed' ends with 'three'?")
log.info(stringTrimmed.endsWith("three"))
//StartsWith method-- checks if string starts with certain characters
log.info("'$stringTrimmed' starts with 'Two'?")
log.info(stringTrimmed.startsWith("Two"))
And here's the output:

Beginning soapUI Scripting 1: Variables and Objects

This is the first of what I hope to be several posts about scripting in soapUI intended for beginners with little to no prior knowledge of Java or Groovy.  While they won't make you an expert, of course, I hope they'll give you enough information to get you on your way quickly.  This particular post will look at a couple of fundamental concepts in Java (and by extension, Groovy), including variables, objects, methods, and dot notation.  These explanations are pretty simplified for the sake of brevity, focusing more on a conceptual understanding than technical details, so I recommend checking out some of the material covered in my post on Java learning resources to dig a little deeper on your own.  The tutorials use soapUI itself, so there's no need to download anything else (I'm assuming you already have soapUI installed).

Groovy is the scripting language used in soapUI.  Running on top of the Java Virtual Machine, it essentially extends and in some ways simplifies Java.  In these posts, I may discuss  concepts as Java concepts, but they apply to Groovy as well.  However, keep in mind that much of the actual syntax you'll see here is specific to Groovy and will not work in Java (for example, lines of code in Java generally require a terminating semi-colon; Groovy script does not, and the examples here reflect that).

SoapUI supplies multiple components for adding Groovy to a project; these include Groovy Script test steps, Setup and Teardown scripts at the test case and test suite levels, and Groovy Script assertions.  For our first example, we're going to use a single Groovy Script test step.  Start soapUI and create a new project, giving it any name you'd like (there's no need to associate it with a web service at this point).  Create a single test case with a single Groovy Script step; again, the names aren't important.  Alternatively, you can download an empty shell project here and import it into soapUI.

Enter the following lines in the main window of your Groovy Script step (be sure to end each line of code with a return):

def myVar = 40 + 2
log.info(myVar)
log.info(log.class)
myVar = myVar - 1
log.info(myVar)

Click the run button at the top of the step window; you should see something like this in the Log Output panel:



It's spectacularly unimpressive, I know, but it illustrates a couple of key concepts.  Let's go line by line:

def myVar = 40 + 2

This line illustrates two key concepts: declaring (basically, creating) a variable and assigning a value to a variable.  A variable is essentially a named placeholder for some data-- by associating the data with a variable, you can reference the data via the variable name.  When you need to create a new variable for use in your script, you can precede it with Groovy's def keyword-- you can think of it as being short for "define"; in this case we're creating a new variable called myVar.  Note that I wrote you can precede it with def; it's not actually necessary to do so, but you'll probably see it used in example Groovy scripts elsewhere (including the soapUI site).

You may understand intuitively what the rest of this line does: it evaluates the statement on the right-hand side of the equal sign, summing 40 and 2, then applies (or assigns) the result (42) to the variable myVar on the left-hand side.  In subsequent lines in our script, we can use myVar to represent the value 42... which is exactly what we do in the next line:

log.info(myVar)

This line prints the value of our variable myVar to the log window.  Like myVar, log is a variable; unlike myVar, however, it was never explicitly created in our script.  The log variable is one of several special variables in soapUI provided for convenience; it's made available automatically-- no need for declarations or assignment statements as with myVar.  Built-in variables are indicated at the top of the Groovy Script step window ("Script is invoked with log, context, and testRunner variables").  Perhaps more importantly, while myVar represents a simple number (specifically, an integer), log represents an object-- a topic which warrants some extra attention as a core concept in Java/Groovy and many other programming languages.

Objects bundle together related data and functions in a meaningful way.  As an example, consider a program that simulates a race between a number of cars.  Keeping track of all the requisite information directly (each car's speed, acceleration, location, direction, etc.) could get unwieldy.  With an object-oriented approach, however, we could create a special "car" data type; each of our simulated cars would be an instance of this data type, and each would be responsible for keeping track of its own information.

In Java, this data type definition or template is called a class.  In this example, we might create a class called Car that has properties corresponding to speed, location, direction, etc.  Additionally, we might create accelerate() and decelerate() functions (called methods when they're part of a class) that, when called, change the speed property accordingly.  Each of our simulated race cars would be a Car object-- each would have all the properties of the Car class we defined, but have its own values for those properties.  Additionally, each could call the accelerate() and decelerate() methods independently, affecting only its own speed property.

So, let's go back to the second line of our script with this extra information about objects:

log.info(myVar)

log is a variable corresponding to an object of the Logger class.  The Logger class has a method, info(), that prints its argument (the data provided between its parentheses-- in this line of code, myVar) as an INFO level message in the log-- consequently, it prints the value 42.  Note the syntax used to call an object's method (sometimes called dot notation): object.methodOfObject([argument(s)]).  Note that some methods may take no arguments, in which case they have empty parentheses.

Moving on to line 3:

log.info(log.class)

This illustrates more dot notation.  Once again, we're calling log's info() method, but this time we're using dot notation to get the class property for log and passing that as the argument to the info() method (the output confirms Logger is indeed log's class).  Note the dot notation syntax for getting an object's property: object.propertyOfObject.

The next line is:

myVar = myVar - 1

This line may look odd-- even incorrect-- at first, with myVar on both sides of the equal sign, but it's perfectly valid and illustrates the way assignment statements are interpreted from right to left. Before this line is processed, myVar has a value of 42.  The assignment statement evaluates the expression on the right-hand side of the equal sign first, subtracting 1 from 42.  The result (41) is then assigned back to myVar.  Note that the def keyword is not used here (compare this to the first line of the script)-- remember, def is only used at the time of a variable's creation; we're not creating myVar here, we're just changing the value it holds.  Its new value is printed to the log in the last line:

log.info(myVar)

I'll try to cover the built-in context variable in a later post, but the testRunner variable is worth a quick look now.  It references a soapUI runner object, which controls test execution and provides access to test results.  The testRunner variable (and the analogous runner variable at the test suite level) can also serve as an entry point to get to other key soapUI objects.  Enter and run the following code in your Groovy Script step:

def myTestCase = testRunner.getTestCase()
log.info(myTestCase.getName())
def myTestSuite = myTestCase.getTestSuite()
log.info(myTestSuite.getName())


The output:



Here we step up through the project tree, calling the getTestCase() method of the built-in testRunner variable to get a test case object corresponding to our test step's parent test case (assigned to the variable myTestCase), then use that test case object's getTestSuite() method to get a test suite object (assigned to myTestSuite).

That's it for now. You can explore some of soapUI's other classes in my posts on scripting objects-- you can find the post on the WSDLTestSuiteRunner class here, for example.  For further reading, I also suggest the Object-Oriented Programming Concepts section of the Java Tutorials.

Sikuli Script: Troubleshooting Problems with Scripts

One of Sikuli's appealing features is its relatively straightforward concept; ironically, that can make it all the more puzzling when things aren't working quite the way they should-- you can see your button right there on the screen, so why can't Sikuli?  This post has a couple of tips for new users for troubleshooting common script problems.

Note: as of this writing, the latest version of Sikuli is version 930, available in Windows as a set of files to be unzipped over the previous version, version 905.  Version 930 includes some significant fixes; if you're experiencing issues and using version 905, I'd recommend upgrading to version 930 as a first step.

If Sikuli can't find a graphic pattern:

1) Be sure to check any FindFailed error messages carefully.  I know, I know-- that may sound obvious, but when you're knee-deep in a scripting session and you've already worked through half a dozen "FindFailed" errors in the last hour, you may see the seventh pop up in Sikuli's log and immediately assume it's the same as the others.  A case in point from my own personal experience: after troubleshooting for some time to figure out why Sikuli wasn't finding a match for my graphic pattern, a closer inspection of the FindFailed error revealed that Sikuli wasn't looking for a graphic pattern at all-- for some reason it was looking for text (treating my pattern file's name as a string).  Among other things, a FindFailed error can also indicate a problem finding a png file in its expected location on disk.

2) If you're looking for the pattern in a defined region (as opposed to a search on the entire screen), make sure the region covers the intended area and fully contains the pattern you're looking for; one way to check this is by calling the highlight() method on your region-- e.g., my_defined_region.highlight(2). You can remove the call later on once you've finished debugging.

3) Don't be stingy with wait times.  Sikuli can interact with an application much faster than a human, and that's not always a good thing.  In some cases it may not find a pattern simply because it hasn't had time to draw completely (in response to some previous action).  Use a simple wait() statement to give the screen, region, etc. more time to draw before proceeding to the next step.

If Sikuli incorrectly identifies a match:

1) A mis-click near a pattern (close but not centered on it) may stem from re-drawing and or re-sizing a window or components in a window. Consider a scenario where Sikuli is waiting for a button to appear onscreen; as soon as it appears it registers the match.  Subsequently, the window finishes drawing, during which the placement of the button is adjusted to make room for other components.  Sikuli doesn't recognize this change in the button's location-- as far as it's concerned, the button is still where its pattern was first detected, and that's where Sikuli will try to interact with it.  Possible solutions in this case include inserting a fixed (designated in seconds) wait() statement or using another pattern as a cue to Sikuli that the screen has finished drawing.

2) If Sikuli keeps matching a pattern to items other than the one you're actually trying to isolate, first make sure your pattern is "focused"-- you generally want to avoid any nondescript surrounding white space that doesn't help Sikuli differentiate it from similar regions.  You can also increase the similarity setting for the pattern to require a closer match.  With your script loaded in Sikuli, bring up the application screen where a match for your pattern should be found and click on the pattern in your script, launching the Pattern Settings dialog.  The Matching Preview tab shows the current screen, highlighting any regions that match your pattern-- if you adjust the Similarity slider, the preview automatically updates to reflect the new value.  Yet another solution: limiting your search to a specific region around your target, excluding other potential matches outside the region.


The Matching Preview tab of the Pattern Settings dialog

For scripting issues not necessarily related to finding pattern matches, the popup() method can be used as a poor man's break point for debugging.  For example, you can use it to pause your script and print out the value of a counter variable in a for loop, a value evaluated in an if statement, etc.  The method can be removed from your final script once you've finished troubleshooting.

Finally, when all else fails or you're seeing completely inexplicable issues, save your work, shut down Sikuli, and check for any orphaned processes (in Windows, these appear as javaw.exe instances) before restarting.  I've seen scenarios where a script didn't seem to shut down properly (particularly when execution was interrupted with Alt + Shift + C) and behavior becomes flaky and unpredictable when I attempt to re-run the script or run another script.  Clearing out these orphaned processes and re-starting Sikuli sometimes helps in these cases.

For Beginners: Java Learning Resources

I've published a few posts on using Groovy in soapUI; as much as I'd like to believe my directions and writing are so clear that no further knowledge is required, I know that's probably not the case-- at the very least, a basic understanding of objects, variables, and other Java programming concepts are necessary, which means that readers who are genuinely new to scripting may have difficulty following some of the material in the posts.  I do want this blog to be a resource for absolute beginners; I may tackle a Groovy tutorial in future posts, but for the time being I thought I'd recommend a few resources for learning Java that I've found helpful.

A logical first step, of course, are the Java Tutorials available online and for download in multiple formats (HTML, mobi, and epub) on Java's home site here.  For beginners or near beginners, the Getting Started, Learning the Java Language, and Essential Java Classes tutorials are recommended.

For third-party publications, I've looked at three notable books on Java, each of which uses a different approach to learning the language.  Learn to Program with Java by John Smiley (Osborne/McGraw-Hill, 1st Edition - 2002) has a very gentle learning curve and an interesting framing premise: the book purports to follow an introductory Java class (taught by the author himself) as they work through creating a practical working Java application.  Head First Java by Kathy Sierra and Bert Bates (O'Reilly Media, 2nd Edition - 2005) takes a more irreverent approach, trying to inject a bit of humor into the topic.  Ultimately, it covers more material than the Smiley book, albeit at a faster pace with a more challenging learning curve.  Finally, there's Bruce Heckel's Thinking in Java (Prentice-Hall, 4th Edition - 2006); coming in at a breezy 1150 pages, this book is pretty in-depth, going beyond the language and syntax to explain what goes on "under the hood" in a Java program-- memory management, the timing of the creation of objects, etc.  This book covers everything you probably wanted to know about Java-- not to mention a lot of stuff you didn't know you wanted to know.  Generously, Mr. Heckel makes the 3rd edition of the book available for free online at his company's (MindView, Inc.) website here.  As I said, all of these books take very different approaches; if possible, try looking at a few sample pages of each to see which best matches your personality or learning style.

If you prefer learning via video, I highly, highly recommend Stanford University's Programming Methodology lecture series available on iTunes U.  Comprised of video recordings of an introductory computer science course, this one is easy to overlook due to its relatively nondescript title.  In fact, while the course is primarily intended to be an introduction to programming concepts in general (hence the name), Java is the language chosen to illustrate those concepts, so it turns out to be a pretty decent introduction to Java specifically.  The lecturer, Professor Mehran Sahami, seems to have genuine affection for his job-- he injects his lessons with plenty of humor and explains things in engaging, easily understandable terms.  My primary issue with the series is the video quality; while it's not a major problem most of the time, text from presentation slides is occasionally hard to read.  The series is popular on iTunes; I hope Stanford might consider re-recording the class sessions in higher quality in the future, although I wouldn't want anything else to change about them.

Of course, this is the tip of the tip of the tip of the iceberg of what's out there, and Java's ubiquity means the list of materials (free and otherwise) will only grow.  Perhaps you have some favorites of your own; are there any Java learning resources you'd recommend to others?