Pages

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

No comments:

Post a Comment

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