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.