Working with Properties with Groovy
//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
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).