Showing posts with label soapUI. Show all posts
Showing posts with label soapUI. Show all posts

JSON Objects in SoapUI

In my last post I started looking at handling JSON response data in Groovy script using the JsonPathFacade class.  In the example I covered there, we checked to make sure all the countries returned in a response matched a search string-- each country entity returned had three properties: a 2 character code, a 3 character code, and a name.  Since the search string was so long (greater than three characters), we only had to check the name property of returned countries.  But what about a search on a shorter two-character string that could potentially match any of a given country's properties-- for example, what if our search string was "kh"?

Here's the corresponding response returned by the service:

{"RestResponse": {
   "messages":    [
      "More webservices are available at http://www.groupkt.com/post/f2129b88/services.htm",
      "Total [2] records found."
   ],
   "result":    [
            {
         "name": "Cambodia",
         "alpha2_code": "KH",
         "alpha3_code": "KHM"
      },
            {
         "name": "Kazakhstan",
         "alpha2_code": "KZ",
         "alpha3_code": "KAZ"
      }
   ]
}}


The method we used before to check names wouldn't work here-- Cambodia is only a match by virtue of its alpha codes, while Kazakhstan's only match is its name.  We have three string properties to check per country instead of one, so a simple one-dimensional array of strings isn't enough.  One approach we can take is to retrieve each country entity as a JSON object and check each object's set of properties.

Here's our new Groovy assertion (line numbers added by me for reference):

01  import com.eviware.soapui.support.JsonPathFacade
02
03  def failCount = 0
04  def jpf = new JsonPathFacade(messageExchange.responseContent)
05  def testArray = jpf.readObjectValue("RestResponse.result")
06  testArray.each {
07    def countryName = it.get("name")
08    def countryAlphaTwo = it.get("alpha2_code")
09    def countryAlphaThree = it.get("alpha3_code")
10    if(!countryName.toUpperCase().contains("KH") 
11      && !countryAlphaTwo.toUpperCase().contains("KH") 
12      && !countryAlphaThree.toUpperCase().contains("KH")) {
13   log.info("    Non-matching country: $countryName")
14   failCount++
15    }
16  } 
17  assert failCount == 0

I'll skip the first few lines (including the import statement and the creation of the failCount and jpf variables)-- these are the same as in the sample assertion from my last post.  The first difference is in line 5 where the readObjectValue() method is used with the JsonPathFacade object to retrieve the array of country entities (using the JsonPath expression RestResponse.result).  The resulting object assigned to the testArray variable is a JSONArray object from the net.sf.json library.  You can find more information about the library at http://json-lib.sourceforge.net.

Like the ListArray class in my last post, the JSONArray class is compatible with Groovy's each() method.  In line 6 it's called to step through the members of the JSONArray's collection of objects of the JSONObject class (also from the net.sf.json library).

The JSONObject class has a get() method that takes a property name and returns the corresponding property value.  In lines 7 through 9 the method is used to get the name, alpha2_code, and alpha3_code properties of each country object, assigning them to the countryName, countryAlphaTwo, and countryAlphaThree variables, respectively.

Once we've gotten the property values into variables, we can check them against our match string.  The if block's conditional statement is a bit gnarly; I've broken it up across lines 10 through 12.  It roughly translates to "if countryName, countryAlphaTwo, and countryAlphaThree do not contain the string 'KH'..."  Note that for each variable, I use the toUpperCase() method to convert all its characters to uppercase before applying the contains() method.  The contains() method is case sensitive, so converting to uppercase (including the match string) "normalizes" everything for easy comparison-- just as easily, I could have used the toLowerCase() method with the match string "kh".

The last few lines are similar to the last post: if the if conditional is true-- if we do indeed find a case where none of the country's properties contain the match string-- then we log information identifying the country (line 13) and increment the failCount variable by one (line 14).  Finally, after wrapping up our each block we assert that our failCount variable still equals zero after checking all the properties for all countries returned by the service.

SoapUI: Handling JSON in Groovy with the JsonFacade Class

As with XML response data, you may come across situations dealing with JSON where you need a little more than what's offered by the standard JsonPath-based assertions. Fortunately, SoapUI provides the JsonPathFacade class for manipulating and retrieving Json in Groovy script, analogous to the XmlHolder class with Xml data.

In my last post, I looked at a service that returned country ISO codes. One type of request searched on a string and returned any countries with matching names, ISO-2 codes, or ISO-3 codes; here's the response JSON for a request searching on the string "United":

{"RestResponse": {
   "messages":    [
      "More webservices are available at http://www.groupkt.com/post/f2129b88/services.htm",
      "Total [5] records found."
   ],
   "result":    [
            {
         "name": "Tanzania, United Republic of",
         "alpha2_code": "TZ",
         "alpha3_code": "TZA"
      },
            {
         "name": "United Arab Emirates",
         "alpha2_code": "AE",
         "alpha3_code": "ARE"
      },
            {
         "name": "United Kingdom of Great Britain and Northern Ireland",
         "alpha2_code": "GB",
         "alpha3_code": "GBR"
      },
            {
         "name": "United States of America",
         "alpha2_code": "US",
         "alpha3_code": "USA"
      },
            {
         "name": "United States Minor Outlying Islands",
         "alpha2_code": "UM",
         "alpha3_code": "UMI"
      }
   ]
}}

Ideally, we'd want to test that all the names returned for the test request do indeed match our input string.  Here's a short example Groovy script assertion that does that using the JsonPathFacade class (line numbers added by me for the walk-through below):

01  import com.eviware.soapui.support.JsonPathFacade
02  
03  def failCount = 0
04  def jpf = new JsonPathFacade(messageExchange.responseContent)
05  def testArray = jpf.readObjectValue("RestResponse.result[*].name")
06  testArray.each{
07    if(!it.contains("United"))
08    {
09      log.info("    Non-matching name: $it")
10      failCount++
11    }
12  }
13  assert failCount == 0

Let's take a look at it line by line:

After importing the JsonPathFacade class (note the XmlHolder class is in the same package) in line 1, line 3 defines a failCount variable we'll use to keep track of failures and check later in our main pass/fail assertion.

In line 4 we create a new JsonPathFacade object and assign it to the jpf variable.  The JsonPathFacade constructor takes JSON response content (as a string) as its only argument.  Within a Groovy script assertion, we can access response content via the responseContent property of the built-in messageExchange variable and pass that into the constructor.

The readObjectValue() and readStringValue() methods of the JsonPathFacade class are the two you'll probably encounter most frequently.  Both take a JsonPath expression (as a string) as input and return the corresponding JSON content; readStringValue() returns the content as a string while readObjectValue() returns it as an object.

In line 5, we use the readObjectValue() method with the JsonPath expression RestResponse.result[*].name to get the names of all the country entities in our response.  This returns a Groovy ArrayList object that gets assigned to the testArray variable.

The ArrayList class is compatible with Groovy's each() method, so starting in line 6 we use it to iterate through the names contained in the testArray variable.  Line 7 checks each name against our match string ("United" in this case).  If the name doesn't match, we log an informational message (line 9) and increment the failCount variable (line 10).

Finally, once our each() method is finished checking all of the country names returned in the response, we assert the failCount variable should still equal 0 in line 13-- if any of the country names didn't contain "United", the assertion and test step fail.

Just to confirm that our script works as expected, let's look at the output if we change our match string (in line 8) to "States", artificially generating failures:


Groovy assertion results and output

Next time we'll look at a more complex example working with JSON objects.

SoapUI: Building JsonPath Expressions

The 5.2 release of SoapUI OS contains some significant changes.  Most obviously, there's been an overhaul of its basic look with new icons and some high-level actions (creating new test suites, importing suites, etc.) added to the main screen.  For the most part, these graphical changes are for ease-of-use and aesthetics; very little has really changed with basic workflows and 5.2 should be compatible with test suites created in pre-5.2 versions.  But in keeping with SoapUI's recent trend of improved support for non-SOAP services, several new JsonPath-based assertion types have also been added:

1) JsonPath Match - Verifies the value specified by a JsonPath expression matches some expected value

2) JsonPath Existence Match - Verifies the element specified by a JsonPath expression exists in the target JSON

3) JsonPath Count Match - Verifies the number of matching instances for a given JsonPath expression (if the expression evaluates to an array, the count is the length of the array) equals some expected value

4) JsonPath Regex Match - Similar to the basic JsonPath Match assertion, but the value specified by the JsonPath expression is matched to a regular expression

For those of you unfamiliar with it, JSON stands for JavaScript Object Notation.  Like XML, it provides a standardized way to represent complex data across different operating systems, architectures, etc.  Here's a typical JSON data block:
{"response":
   {
      "library":[
         {
            "title":"War and Peace",
            "author":"Tolstoy, Leo",
            "pages":1296,
            "tags":["literature","Russian","classic"]
         },
         {
            "title":"Harry Potter and the Chamber of Secrets",
            "author":"Rowling, J.K.",
            "pages":341,
            "tags":["fiction","fantasy"]
         },
         {
            "title":"In the Garden of Beasts",
            "author":"Larson, Erik",
            "pages":448,
            "tags":["non-fiction","history"]
         }
      ],
      "requestId":12345,
      "requestStatus":"OK"
   }      
}
This JSON defines an object (enclosed in braces in JSON) called response that consists of library, requestId, and requestStatus properties.  The library property is an array (enclosed in squared brackets) of objects representing books.  Each book object has a title, author, pages, and tags property; the tags property is itself another array of strings.

JsonPath is JSON's equivalent of XPath-- it provides a simple way to reference a particular value in JSON.  You can read a detailed description of JsonPath at http://goessner.net/articles/JsonPath, but I'll present some quick starting rules here with examples using the JSON data above.  JsonPath actually supports two different basic syntaxes, one using bracketed property names to navigate through successive levels of data (e.g., ['parent']['child']), and another using parent.child dot notation syntax similar to what's used in most programming languages.  For the sake of brevity, I'll focus on dot notation syntax in this post.

DescriptionExample ExpressionExample Result
To get the value of an object's property, separate the property name from the object name (or the portion of the JsonPath expression designating the object) with a dot-- again, if you're familiar with dot notation in programming, it's pretty much the same idea.response.requestStatusOK - The response object's requestStatus value
Zero-based indexing using brackets indicates a particular element in a JSON array.  Note the use of zero-based indexing (i.e., representing the first item with index 0, the second with index 1, etc.) is a key difference from XPath's one-based indexing.response.library[0].authorTolstoy, Leo - The author property of the first object in the library array
Using * instead of an index number with an array returns all elements in the array.response.library[*].title[War and Peace, Harry Potter and the Chamber of Secrets, In the Garden of Beasts] - A JSON array of every title in the library array
The JSON data root is designated by $, although this is optional in expressions that explicitly start with the first element of the target JSON data (the examples given above safely omit the $ since they start with the root response element).$.response.requestStatusOK - Equivalent to the first example; the requestStatus property of the response object
A pair of dots indicates a recursive search for the following property; i.e., the property can be found at any level beneath the property preceding the dots.  This is equivalent to double slashes in XPath.$..requestId12345 - The response object's requestId property (note we didn't have to explicitly designate the response object here)
You can access the length property of an array to determine the index of its last (or some position relative to the last) member.  In the example expression, @ represents the last evaluated element-- in this case, the library array-- in the bracketed expression.  Remember that because of zero-based indexing, the index of the last member of an array is one less than the array's length, so we subtract 1 from the length to find its index.  JsonPath is very particular about brackets and parentheses; the parentheses around the @.length - 1 expression may seem optional, but omitting them will result in an "invalid expression" error.$..library[(@.length - 1)].authorLarson, Erik - The author property of the last member in the library array.
You can also build basic predicate statements within squared brackets to filter on specific criteria.  The ? enclosed in brackets indicates a predicate, followed by a test expression in parentheses.  Within the predicate expression, the @ character acts as a placeholder for each array member being evaluated by the predicate.  So in this example expression we're starting with the result array, then evaluating each member's name property and returning those members where the title property matches 'War and Peace' (note the use of == instead of = to check for equality here).  Finally, for each array member that passed the predicate test, we get the value of its pages property.result.library[?(@.pages > 400)].title[War and Peace, In the Garden of Beasts] - The title property of every book object with a pages property greater than 400

If you'd like to play around with JSON expressions yourself, you can copy and paste the JSON data above into the JsonPath expression tester at http://jsonpath.curiousconcept.com.  One thing to keep in mind when working with the JsonPath expression tester: string values returned by JsonPath expressions in SoapUI are not enclosed in quotes, as they are with the JsonPath tester (the examples above show results as they appear in SoapUI).

SoapUI: Using FileAppender for Logging

In writing SoapUI test scripts, you may come across a situation where you need some additional logging functionality; for example, writing information to a file aside from the default SoapUI log for review after running a script via testrunner.  You could explicitly create and manage your own text file, but you may find it simpler to leverage the existing Logger object (via the log variable) to send logged messages to another file using the FileAppender class.

A FileAppender can forward messages sent to the log to a separate file, using formatting options to label messages with dates, logging levels, etc.  Here's an example setup script demonstrating how to set up a FileAppender to write to a simple text file (you can download a starter project containing all this code here):

import org.apache.log4j.FileAppender
import org.apache.log4j.PatternLayout

//Define the layout to use
def pLayout = new PatternLayout("%d{DATE} : %m%n")
//Create FileAppender in context using layout we just defined
context.customLogger = new FileAppender(pLayout,"C:\\Temp\\CustomLog\\CustomLog.txt", false)
//Add the appender to the existing log
log.addAppender(context.customLogger)

Remember, there's no need to add any additional jars to SoapUI's ext folder in this case; we're using the log4j package that's already part of its libraries.  However, we do need to import the FileAppender and PatternLayout classes at the top of our script.

After the import statements, a new PatternLayout is created for use with the FileAppender:

def pLayout = new PatternLayout("%d{DATE} : %m%n")

The string that's passed into the PatternLayout constructor ("%d{DATE} : %m%n") is used to describe how we'd like information formatted in the log file.  For a detailed list of the special formatting indicators (generally demarcated by the % character), see the javadoc for the log4j PatternLayout class.  In the meantime, here's a table showing some example patterns and how the line log.info("Simple Test Message One") is rendered in a custom log (starting with the pattern used in our script):

PatternOutputNotes
%d{DATE} : %m%n
14 Sep 2015 21:51:21,223 : Simple Test Message One
- %d indicates a date, with {DATE} specifying a log4j pre-canned date format
- %m indicates where the actual message content will be shown
- %n indicates a new line character
%-15d{ABSOLUTE}%p : %m%n
16:25:33,000   INFO : Simple Test Message One
- {ABSOLUTE} is used with %d to specify another log4j pre-canned date format-- this one prints only the time component of the {DATE} default format used in the first example.
- The -15 at the beginning of the date pads the expression, reserving 15 characters for the date and right-padding with spaces if the date is less than 15 characters long.
- %p indicates the priority level of the message (e.g., INFO, DEBUG, etc.).
%26d{ISO8601} : %m%n
   2015-09-19 16:50:08,095 : Simple Test Message One
- {ISO8601} is the last of the log4j pre-canned date formats, printing the date portion with numeric year, month, and day. ISO8601 is also the default format used if none is specified.
- The 26 at the beginning of the date format expression pads the date expression like -15 in the previous example, but the positive value pads the left instead of the right (note the leading spaces in the output).
%d{MM-dd-yyyy HH:mm:ss.SSS} : %.10m%n
09-19-2015 17:31:10.976 : essage One
- In this case we're using a custom format for the date; this one prints the date with numeric month, then day, then year, then prints the time using a decimal point as the divider between seconds and nanoseconds. You can find out more about the date and time pattern characters to use for a custom date format here. According to the log4j javadocs, using a custom format like this could result in a performance hit; they encourage you to use the pre-canned formats instead.
- The .10 before the message specifier is another spacing indicator; this one specifies that if the message is longer than 10 characters, it should be truncated from the front (which is why you only see the last 10 characters in the output).

Once we have our PatternLayout created, we can create our FileAppender, using the PatternLayout as an argument in its constructor.  We put it in context to manage disposing its resources later on in the tear down script:

context.customlogger = new FileAppender(pLayout, "C:\\Temp\\CustomLog\\CustomLog.txt", false)

The first two arguments should be self-explanatory: the first is the layout we want the FileAppender to use and the second is a string giving the path and name of the text file where we want the FileAppender to forward messages-- feel free to change this path as you see fit.  Remember that "\" has a special meaning in string in Java; that's why we have to use the escape sequence "\\" here instead.  The final argument specifies whether we want the messages appended to the text file.  When set to true, any content in the text file generated in previous runs is preserved and information from this run is added to the end of the file.  In this example we're setting the argument to false, so each time we run our suite a new log is created.

The final line in the setup script registers the appender with the default SoapUI log.

log.addAppender(context.customLogger)

Once everything is configured and the appender is registered with the log object, there's nothing special you have to do to send messages to this custom log file-- anything sent to the log within your script is sent to the FileAppender's underlying stream.

As indicated in the table above, the line log.info("Simple Test Message One") in our script results in this output in the custom log file:

14 Sep 2015 21:51:21,223 : Simple Test Message One

Within our tear down script (or whenever we're done with the appender), we have to release the resources being used by the appender, closing the underlying file and the appender itself:

context.customLogger.closeFile()
context.customLogger.close()

I encourage you to play around with the provided starter project to see what can be done with a file appender.  In particular, try making changes to the pattern layout string to see how it controls formatting and affects the custom log's output. 

SoapUI: Writing Results to Excel

SoapUI's testrunner can be used to run test suites from the command line and provides an option to generate jUnit-style test reports.  These can be useful, of course, but they're not always easily readable.  In this post I'll cover how to collect test results and record them in an Excel document using the Apache POI API, downloadable here.  As a bonus, I'll also show some examples of Groovy closures in action to help streamline our script and avoid repetition.  Fair warning: this post's a bit long, but I really couldn't think of a good place to try to break it up.

I'll be building on the data-driven periodic table project using an Excel file as a test source; you can download this extended version of the project here.  Unzip the files (the project and source xls file) and import the project into SoapUI.  Modify the set up script for the ElementSpecificRequests test case to specify the location where you put the source data file (remember to double slashes in the path to escape them); likewise, you can change the location where you want to output your report file by modifying the test suite tear down script.  Run the suite, and it should create an Excel workbook containing test suite step results in the location specified in the suite tear down script.

For the sake of brevity I'll focus primarily on what's changed or new; you can read or review the details of the unchanged bits in the original post.  Setting up a project to use the API is also covered there: remember that to use the POI API library (or any third party library) within SoapUI, you need to drop its jar file into the bin/ext directory in your SoapUI program folder (a re-start of SoapUI may also be necessary).  This makes it possible to import and use the library's resources in your scripts.

Since we'll need to write to our report from both test cases, the code to set up the report is at the suite level.  Here are the first few lines of the set up script for the suite:

import org.apache.poi.hssf.usermodel.*

//Create report output workbook and worksheet
context.reportWorkbook = new HSSFWorkbook()
def reportSheet = context.reportWorkbook.createSheet("PeriodicTable-TestReport")

The import statement makes the members of the org.apache.poi.hssf.usermodel package available in our script-- components related to working with xls files.  A new HSSFWorkbook object is created and a sheet ("PeriodicTable-TestReport") is added to the workbook.  Note that the workbook is assigned to a context property (reportWorkbook) so it can be referenced again in the tear down script.

After the report workbook and worksheet are set up, we come to the first closure.  Closures are blocks of code that can be treated as pieces of data; i.e., they can be assigned to variables and properties, used as arguments to methods, etc.  While not the same as traditional Java functions, closures in practice behave very similarly.  In this case, a closure is used to encapsulate the code to write test results to the report worksheet to avoid re-writing the code in multiple places.  I'll review the closure in two parts:

//In the following closure tsResult represents a TestStepResult object
context.recordResults = {tsResult ->
   //Create new row in output workbook
   def newRow = reportSheet.createRow(reportSheet.getPhysicalNumberOfRows())
   def nameCell = newRow.createCell(0, HSSFCell.CELL_TYPE_STRING)
   def timeStampCell = newRow.createCell(1, HSSFCell.CELL_TYPE_STRING)
   def resultCell = newRow.createCell(3, HSSFCell.CELL_TYPE_STRING)
   def timeCell = newRow.createCell(2, HSSFCell.CELL_TYPE_NUMERIC)
   def assertFailCell = newRow.createCell(4, HSSFCell.CELL_TYPE_STRING)

   //Write test result data to corresponding cells in the newly created row
   def statusString = tsResult.getStatus().toString()
   nameCell.setCellValue(tsResult.testStep.testCase.name + "-" + tsResult.testStep.getName())
   resultCell.setCellValue(statusString)
   timeCell.setCellValue(tsResult.getTimeTaken())
   timeStampCell.setCellValue(new Date(tsResult.getTimeStamp()).toString())

The closure itself is demarcated by braces ({ and }), and the entire thing is assigned to the recordResults context property to facilitate re-use across test cases.  Note the odd-looking bit at the beginning of the closure (just after the opening brace): tsResult ->; this designates tsResult as the variable name used within the closure to represent the argument we'll eventually pass into it.  If this doesn't make sense just yet, it should be a little clearer once you see an example of calling the closure a little bit later.

The next non-comment line create a new row in the worksheet (newRow).  The createRow() method requires a zero-based index value to designate where in the sheet the row should be created (0 is the first row of the sheet, 1 is the second row, etc.).  Since we want to add in each new row after the existing rows, we use the getPhysicalNumberOfRows() method of the worksheet object to get the correct index we should use.  So, for example, if we have 6 rows already added in the worksheet, the physical row count (which is not zero-based-- it uses natural counting starting at 1) is 6.  The zero-based index of the last row is 5, hence the zero-based index we need to use for the next row is also 6.

The next few lines create the cells in the new row corresponding to test result data.  The createCell() method takes an index argument (zero-based again) specifying the column in the row where the cell should be created and a data type argument.

Finally we retrieve the test result data from the tsResult variable (which represents a TestStepResult object) and set cell values accordingly.  We're writing a test step name (created by combining the name of the step's parent test case with the name of the step separated by a dash), status, time taken, and time of execution.  There's a little bit of extra work involved in getting a string representation of the time of execution.  The time stamp is returned in milliseconds by the getTimeStamp() method.  That's used to create a new Date object, and that object's toString() method is called.

The last cell in the row, assertFailCell, should contain a list of any assertions (by name) that failed for a given test step.  The code to get that list of assertions makes up the second part of the closure:
 
   //Code to get the names of any failed assertions
   def failedAsserts = "" 
   if(statusString != "OK"){
      def assertList = tsResult.testStep.getAssertionList()
      assertList.each(){
         if(it.status.toString() != "VALID"){
            if(failedAsserts == ""){
               failedAsserts = it.name
            }else{
               failedAsserts += (", " + it.name)
            }
         }
      }
   }

   //Write the list of failed assertions to the appropriate cell
   assertFailCell.setCellValue(failedAsserts)
}

After defining a string where we can create our list of assertions (failedAsserts), there's an if block that contains code that's only executed if the value of statusString is not "OK"-- in other words, when the test step did not pass.  If the step didn't pass, we drill down into the step's assertions, getting the step result's parent test step, then using the step's getAssertionList() method to retrieve a list of assertions.

Once the list is assigned to the variable assertList, we call its each() method.  This is one of several special methods available to aggregate data structures (i.e., data structures like lists, maps, etc., capable of containing multiple values).  The each() method takes each member of the list and performs some action on it.  The action to perform is specified in a closure defined immediately after the method call-- note the opening brace after each() marking the beginning of the closure. Each member is passed as an argument to the closure and assigned to the variable it within the closure-- it is the variable name used when another variable name is not explicitly designated (as we did above with the tsResult variable).

In this case each assertion's status is checked.  If it's not equal to "VALID" (i.e., the assertion did not pass), the name of the assertion is retrieved and added to our comma-separated list of failed assertion names.  Once we've finished building the list, it's written to the assertFailCell cell in our report worksheet and the recordResults closure is finally ended with a closing brace.

I think it's important to point out that none of the code in the recordResults closure is actually executed here.  The closure is just defined the same way we might define a string or number variable or property for use elsewhere later on.

The last lines of the test suite set up script create a header row for our test report sheet:

//Write out report column headers to the report sheet
def headerRow = reportSheet.createRow(0);
def testNameCell = headerRow.createCell(0, HSSFCell.CELL_TYPE_STRING)
def timeStampCell = headerRow.createCell(1, HSSFCell.CELL_TYPE_STRING)
def testTimeCell = headerRow.createCell(2, HSSFCell.CELL_TYPE_STRING)
def testResultCell = headerRow.createCell(3, HSSFCell.CELL_TYPE_STRING)
def failedAssertions = headerRow.createCell(4, HSSFCell.CELL_TYPE_STRING)

//Populate header row cells with column names
testNameCell.setCellValue("Test Name")
testResultCell.setCellValue("Pass/Fail")
testTimeCell.setCellValue("Time Taken (ms)")
timeStampCell.setCellValue("Test Date/Time")
failedAssertions.setCellValue("Failed Assertions")

The suite tear down script is much shorter and should be relatively self-explanatory-- it writes the workbook data out to disk (modify the path string used to create the FileOutputStream object to change your output location and/or file name) and then closes the output stream used to write the data:

import org.apache.poi.hssf.usermodel.*

//Workbook exists in memory; write it out to a file here
def fout = new BufferedOutputStream(new FileOutputStream("C:\\Temp\\SoapUIRunReport.xls"))
context.reportWorkbook.write(fout)
fout.close()

Finally, we'll look at some code that actually uses the closure we defined above-- and you'll see just how useful closures can be.  Here's the code that's been added to the ReadNextLine Groovy script step in the ElementSpecificRequests test case:

def resultsList = testRunner.getResults()

context.recordResults(resultsList[-3])
context.recordResults(resultsList[-2])
context.recordResults(resultsList[-1])

This code gets the results from the three request test steps in the test case and writes their data (step name, time stamp, etc.) to the report workbook-- and it does all of this in four lines of code, thanks to the recordResults closure we created above.  There's a single line in the tear down script of the GetAtoms test case that does basically the same thing; however, the ElementSpecificRequests test case is a slightly more interesting example.

The first line calls the testRunner object's getResults() method to get a list of TestStepResult objects.  The last three lines take the last three members of that list and pass them as arguments to the recordResults closure.

You may be wondering what's up with the negative index numbers used to get the members of the list.  Because of the way we've implemented our data-driven testing, looping over the test case's test steps repeatedly, test results accumulate.  For example, the first time we reach the ReadNextLine script step, three step results have been recorded for the test case.  After looping through again, when we reach the ReadNextLine step we'll have seven step results for the test case (results for all four steps from the first time through, plus the first three steps for the second time through), and so on.  So each time through we only want to grab the last three available steps in the list of results, corresponding to the three request steps for the current loop.  Negative indexing allows us to do just that: using an index of -1 (resultsList[-1]) gets the last member of the list, an index of -2 gets the second to last member of the list, etc.

So once we have the last three members we can pass them to the closure.  In terms of syntax, you can see it looks just like calling a method-- we put our argument(s) to the closure inside parentheses after the name of the closure.  Scroll back up and take a look again at the recordResults closure to get a better idea of what's happening.  Remember we defined tsResult as the name of the variable holding our argument within the closure; when we pass resultsList[-1] into the closure, the last available TestStepResult object is assigned to that variable and the code in the closure is executed.

If you compare this project against the original data-driven xls project, you'll see that I've also made some changes to the code used to read data from the source file, re-organizing it into a closure so duplication of some code is avoided.  As this post is quite long enough already, I'll leave it to you to identify those changes on your own.

Automating SoapUI with Testrunner

In the SoapUI examples I've provided so far, test suites have been run through the UI.  At some point, however, you'll probably want to take your automation to the next step, running suites unattended and reviewing test results later.  We'll take a look at those topics over the next few posts, starting with SoapUI's testrunner.

Testrunner.bat typically resides in the bin sub-directory of SoapUI's program folder-- e.g., C:\Program Files (x86)\SmartBear\SoapUI-5.0.0\bin.  It allows you to run a SoapUI test suite from the command line with a variety of switches to set configuration options-- quite an extensive variety, in fact-- you can find the full list on the testrunner page on SoapUI's site.  Note that some of the switches listed here are only available in the Pro version of SoapUI.

Here's an example of a typical command sequence illustrating some of the more common switches you'll probably end up using:

"C:\Program Files (x86)\SmartBear\SoapUI-5.0.0\bin\testrunner.bat" -s"periodictableSoap TestSuite" -fC:\Temp\TestOutput -I -j -M C:\My Documents\MyPeriodicTableProject.xml

Going through each element of the sequence:

- "C:\Program Files (x86)\SmartBear\SoapUI-5.0.0\bin\testrunner.bat" : This is simply the path to testrunner.bat.  For those of you unfamiliar with using the command line, note the path is enclosed in quotations because it contains spaces.  Spaces normally separate elements of a command sequence; surrounding the path in quotations ensures the full path is treated as a single element.  Also, the full path is unnecessary if you've changed the working directory to the bin directory (i.e., you'd only have to specify "testrunner.bat"-- you can see this in the SoapUI TestRunner dialog below).

- -s"periodictableSoap TestSuite" : The -s switch precedes the name of the test suite within your project that you'd like to run-- as with the path to testrunner.bat, the name of the suite is enclosed in parentheses because it contains a space.

- -fC:\Temp\TestOutput : The -f switch precedes the path to the directory where test results will be saved.

- -I : This switch instructs testrunner to continue running even if an error is encountered.  Switches are case-sensitive (-i has a different meaning).

- -j : While SoapUI Pro supports a number of additional switches to generate different types of reports, the -j switch is available in the free version to generate JUnit-style xml reports.  These reports are saved to the directory specified with the -f switch above.

- -M : Separate from the JUnit-style report enabled by the -j switch, the -M switch saves a run log report to the results directory.

- C:\My Documents\MyPeriodicTableProject.xml : Finally, we specify the path to the project containing the suite to be run (no switch needed here).

Until you're comfortable with all of its switches and constructing a command sequence from scratch-- or just as a matter of preference-- you can also launch testrunner via the UI to let SoapUI generate a command sequence for you.  In the workspace tree, right-click on the name of the test suite for which you'd like to generate your command sequence (or the test project if you'd like to run multiple test suites in a given project) and select "Launch TestRunner" to bring up the configuration dialog.


Select your options from the various tabs in the dialog (the Basic and Reports tabs contain the more commonly used settings) and click the Launch button.  A command sequence corresponding to the selected configuration options is displayed near the top of the testrunner output (highlighted in the screenshot below).


Once you have this command line sequence it opens up a wide range of possibilities: you can incorporate it into your own batch files or Powershell scripts, integrate SoapUI test runs with an automation solution like Jenkins (incidentally, you can also use Jenkins to publish JUnit reports), schedule a run with Windows Task Scheduler, etc.

As mentioned above, the free version of SoapUI supports JUnit-style reports out of the box; however, it's possible to create some home-brewed reports using Groovy scripting.  We'll take a look at writing results to XLS files using the POI API in the next post.

XQuery in soapUI Part 2: Where and Order By Clauses

In the last post we took a look at the basic syntax of XQuery expressions in soapUI, including for and return clauses.  In this post we'll look at the optional where and order by clauses, which can add more complexity to expressions.

The XQuery where clause is very similar to the SQL where clause; it allows you to provide an additional filter on the base data set selected in your for clause.  In the last post we created an XQuery example using the Euro 2012 web service that retrieved data for each player returned in the AllPlayersNames test request.  Let's say we wanted to narrow down the list of players to include only members of the German national team.  We could adjust the query by adding a where clause just after the for clause:


In this case the where clause (where $x/m:sCountryName = 'Germany') whittles down the full set of tPlayerNames elements returned by the for clause, keeping only those that satisfy the condition of the where clause (in this case, those where the sCountryName child element equals "Germany").

Many of the same functions available in XPath expressions can also be used in XQuery expressions; the following is a valid where clause, for example:

where starts-with($x/m:sName/text(),'M')

The starts-with function takes two arguments and returns true if the first argument (in this case, the value of the sName element) begins with the second argument ("M").

In many cases, the choice to use a where clause may come down to preference; it's possible to achieve the same end result by using predicates with your for clause XPath expression.  This XPath expression in the for clause returns only German players (equivalent to the first where clause example):

for $x in //m:tPlayerNames[m:sCountryName='Germany']

Likewise, the following is equivalent to the second example, returning only those players with names starting with "M":

for $x in //m:tPlayerNames[starts-with(m:sName/text(),'M')]

For readability, however, you may find using where clauses preferable to the extended for clauses.

If a web service returns data in an arbitrary order (i.e., changing with each request), it presents a problem for XQuery assertions, which check for an exact match-- order absolutely matters.  For situations like these, an XQuery order by clause is necessary.  As you might expect, the order by clause is used to sort XQuery results by some node's value, ensuring results are always in the same order for evaluation by the assertion.  The order by clause is added before the return clause; here's an example of a complete XQuery expression including a for, where, order by, and return clause:


This example selects tPlayerNames elements from the response (the for clause), filters out only those elements with sName child element values that start with "M" (the where clause), orders the results by the value of the sName child element (the order by clause), and finally returns the values of the sName and sCountryName child elements for each tPlayerNames element, formatted within identifying tags (the return clause).

It's also possible to specify multiple values for the order by clause, establishing primary, secondary, etc. sort criteria.  For example, you can change the order by clause in the example above to the following to use sCountryName as the primary sort value and sName as the secondary sort value:

order by $x/m:sCountryName/text(), $x/m:sName/text()

The beginning of the results from this modified XQuery expression look like this:


As expected, results are sorted by country first, with players from the same country further sorted by name.

As usual when discussing XML-related topics, I highly recommend checking out W3Schools.com (available from the Useful Links section on the right) to find more information about using XQuery expressions.

XQuery in soapUI Part 1: For and Return Clauses

A few weeks ago I spent a few posts looking at XPath assertions in soapUI; in this post I'll take a look at XQuery assertions.  XQuery assertions can be used to retrieve data from XML using a SQL-like syntax.  The advantage of XQuery over XPath is that it can be used to check multiple node values with a single expression.  XPath expressions in soapUI must resolve to a single value (a count, a Boolean, a node, a node value, etc.); while you can use an XPath expression to validate some large chunk of static data, XQuery provides a little more flexibility, allowing you to pick and choose the nodes you'd like to have returned-- particularly useful in cases where there's a mix of static and dynamic data.

For an example, let's look at the Euro 2004 web service I introduced a few posts back and look at the AllPlayerNames request.  You can download a stripped down "test suite" consisting of a single test case here.  Import the project into soapUI and run the test request; it should return a list of participating players in the Euro 2012 tournament:


Let's say (strictly for the sake of this example) we're only interested in validating the iId, sName, and sCountryName values for each player, verifying they match their expected values.  Expand the Assertions panel for the test request, click the icon to add a new assertion, and select the XQuery Match assertion type (part of the Property Content group in the Add Assertion dialog) to bring up the assertion editor.

As with XPath assertions, you'll need to make some namespace declarations; click the Declare button at the top of the editor to let soapUI do this for you.  The XQuery expression below returns the iId, sName, and sCountryName values for all players; enter it beneath the namespace declarations as shown:


Click the "Select from current" button to evaluate the XQuery expression.  Here's a snippet of the results:


Let's break down the expression, starting with the for clause: for $x in //m:tPlayerNames.  If you're familiar with for-in or foreach-in loops in some programming languages, used to iterate through a collection and perform some action on each of its members, this is a bit similar.  The for clause here essentially translates to "for each member in the set of elements returned by //m:tPlayerNames..."  The clause also assigns those elements to the variable $x in subsequent lines of the expression.

The return clause does the heavy lifting, retrieving data associated with each element via XPath expressions.  For example, $x/m:iId/text() returns the value of the iId child element for each element. The text() function may be new for some-- it simply indicates that you want the actual inner value (i.e., the value between tags) of an element.  With soapUI's XPath expressions, the text() function isn't necessary (specifying an element is enough to indicate you want its value and not the node itself), it is required for XQuery expressions.

Tags are used to group and organize expression results.  Experimenting a little bit with the expression, you'll see a couple of general rules.  If the XPath expression in the for clause returns multiple nodes, the entire expression should be wrapped in tags (like the "playerList" tag above).  Likewise, you'll receive an error if you try to alter the example expression by removing the "player" tag around the return XPath expressions-- because there are multiple XPath expressions applied to each element, a tag is needed to separate each element's returned data (each id, name, and country set).  The "id", "name", and "country" tags are not in fact necessary, but they make the results more readable.

Within tags, brackets ( "{" and "}" ) are used to identify expression content that needs to be evaluated, as opposed to literal text, which can also be included between tags.  Here's a variation on the example expression that includes some literal text:


Here's what the result looks like:


As you can see, the literal characters within the tags (but outside the brackets) are left intact in the expression output.

Now that you're familiar with the basic syntax of XQuery expressions, we'll look at where and order by clauses in the next post.

REST Services and JSON in soapUI Test Suites

In the previous post I walked through adding a REST service to a project in soapUI; as a quick recap, here's a screenshot of where we left the project, with several REST requests:


As with SOAP services, once a REST service has been added to a project soapUI can automatically generate a test suite for the service.  Right-click on the service's node in the workspace tree and select "Generate TestSuite" from the context menu.  The Generate Test Suite dialog looks very similar to the corresponding dialog for SOAP services, allowing you to choose the resources for which you'd like to generate tests, whether you'd like all of the generated tests grouped into a single test case or assigned to their own test cases, etc.:


Let's leave the default options and click OK.  A test suite is generated with one test case for each of the REST service resources we defined previously.  By default, test requests use the parameter values that were set when the corresponding service request was added to the project.  However, these values can be changed in a test request's parameters table, and you can use them with property expansion.  Open the cardId test case and create a test case property called testCard and give it the value "chain-reaction".  Open the editor for its lone test request (Retrieve Individual Card Info) and use soapUI's property expansion syntax to specify the testCard property of the test case as the value of the test request's cardId parameter-- enter "${#TestCase#testCard}".  Click the Send button (the green arrow) for the test request.  Looking at the response, we can see that the data returned does indeed correspond to the card named Chain Reaction:


Remember that you can specify different parameter types in the parameter table, so you can use property expansion with any of them: URI parameters, resource path parameters, request body parameters, etc.

Let's move on to creating an assertion.  Up until now in my posts I've focused on web services that return response data as XML; this REST service gives us an opportunity to look at how soapUI handles JSON data.  SoapUI's solution for dealing with JSON data is really pretty straightforward-- it generates an XML representation of the same data set.  Consequently, all of the same XML-based content assertion types are available to use with JSON response data.  Note the tabs running along the left side of the panel showing the response data, and (if necessary) click on the one labelled "XML" to view the response in XML format.


Click on the Assertions button at the bottom of the Request Editor to open the Assertions panel, click the Add Assertion button, and add a new XQuery Match assertion.  The service returns data for multiple editions of the same card, so we'll use our assertion to confirm some expected values for the two editions of the Chain Reaction card.  Click the Declare button at the top of the assertion's configuration dialog to add a namespace declaration and add an XQuery expression to return data for the card's editions; the expression with its declaration should look something like this:


Click the "Select from current" button to capture the expected result of the expression, then save the assertion with the test request.  When dealing with XML-based assertions and JSON response data, note that even though the XML is auto-generated by soapUI, a namespace declaration and namespace prefixes are still needed.  Also be careful of extra tags in the XML (such as the "e" tags around each set of edition data) that do not exist in the original JSON but are inserted by soapUI to delineate some JSON data structures in the conversion.

Adding REST Services in soapUI

In this post I'll take a long overdue look at REST (REpresentational State Transfer) services in soapUI.  For anybody new to REST, I recommend this article covering the basics.  REST is not a web service protocol in and of itself; it's a web service architecture that can be implemented with any protocol, although it's frequently implemented with HTTP.  Occasionally, there's some confusion that arises from this relationship-- something to remember when thinking about your approach to testing a service.  For example, you may encounter a simple HTTP service labelled as a REST service that doesn't really take advantage of REST's architectural principles.  In these cases, it may be easier or more straightforward to implement test requests as basic HTTP requests in soapUI.

The last few releases of soapUI have also been very REST-centric, focusing on improved support and ease-of-use when dealing with REST services.  Going back just a few releases, creating REST services in a soapUI project was much more involved and complicated.  Consequently, if you intend to work with REST services I recommend updating to the most recent version if you haven't done so already.  This post reflects the workflow in soapUI 5.0.

For these examples, I'll be using the REST service at api.deckbrew.com.  The service provides information about cards from a popular collectible card game (Magic: The Gathering).  I apologize-- for most of you this subject matter will probably seem very dry, but it was surprisingly difficult finding a freely accessible web service that provided resources that could be used to create some meaningful examples.  This service is also limited in that it only leverages the HTTP GET method; an ideal REST service example would also utilize other HTTP methods (corresponding to different actions that could be taken on the service's resources).

When adding a SOAP service to a project, soapUI uses its WSDL document to determine the service's operations, their parameters, etc.  REST services have WADL (Web Application Description Language), which is roughly equivalent to SOAP's WSDL, but frequently a WADL document is not provided for a REST service (as is the case with our example service).  In these cases, soapUI allows you to enter service requests and parses the requests to identify the service's components.

From the soapUI File menu, select the option to create a new REST project.  You'll be prompted to enter a URI for the service:


To generate a request for a filtered card search, enter the following for the URI (as one continuous line):

https://api.deckbrew.com/mtg/cards?page=1&type=&subtype=&supertype=&name=&oracle=&set=&rarity=rare&color=red&multicolor=false&multiverseid=&format=&status=legal

In the workspace tree, soapUI generates a new project containing a node for the service's Cards resource, along with method and request child nodes corresponding to the URI we entered.  Click on the request to see the generated GET request:


All of the parameters entered as part of the request URI (including those for which no value was provided) are listed in the Request Editor.  You can add extra information about the parameters via the panel below the table; designating whether or not a parameter is required, for example.

The Level column determines the parameter's "scope" within the service-- a parameter can be associated with the RESOURCE level or the METHOD level.  Associating it with the RESOURCE level means the parameter is automatically "inherited" by any other method(s) created for its parent resource; it's added by default.  Associating it with the METHOD level means the parameter is only applicable to that method.  For this example, we'll just leave the parameters set to the default (RESOURCE).

A parameter's Style column indicates how the parameter is implemented-- the QUERY style corresponds to the standard "?parameter1=value1&parameter2=value2..." format you see as part of this GET request's URI.  Although POST requests are not supported by this example REST service, the QUERY type is also used when sending parameters in this format as part of the body (as opposed to the URI), as with POST requests.  For a POST request (and other methods allowing parameters in the message body), a checkbox is displayed that allows you to specify that parameters should be added to the request body instead of the URI.


To see an example of a different style, let's try adding a request to get the details for a specific card.  Right-click on the service in the tree and select "New Resource" from the context menu.  Another prompt appears-- note that once the host for the REST service has been established (via our previously created request), it's no longer necessary to enter the full URI when adding more resources.  The resource path by itself is sufficient.  Enter the following in the prompt:

/mtg/cards/about-face

You'll be asked if you want to add the new resource as a child of the existing Cards resource.  The parameters we added for the Cards resource aren't relevant to the single card request, so we're just going to add it as a completely separate resource.  Click No to the prompt.

There are no URI parameters for this request.  Instead, the last part of the resource path is the parameter.  We used "about-face" as the individual card id, but we can change the id to retrieve information for other cards.  This is where the TEMPLATE parameter style comes into play.  Open up the Request Editor dialog and add a new parameter called cardId.  Set its value to "about-face" and set the parameter's style to TEMPLATE.  You'll see the parameter appended to the resource path at the top of the dialog, enclosed in braces.  Edit the path to remove the hard-coded text "about-face", leaving the parameter to indicate the card id to retrieve:


After adding some additional resources and modifying some names to make them more descriptive, here's what our service looks like in our project:


In the next post, I'll look at using the REST service in a test suite.

Beginning soapUI Scripting: Javadocs 2

In my last post I started a discussion of javadocs, providing a high level overview. In this post I'll try to cover some of the keywords and concepts you may encounter when dealing with documentation for a class.

I have to admit I may have bitten off more than I can chew with this post, which accounts in part for the length of time it took me to publish it.  As I started looking at javadocs and javadoc keywords in more detail, I realized the post had to include explanations of a lot of basic Java concepts.  I hope I did an adequate job of explaining them, but I strongly encourage you to check out the official Java Tutorials to fill in the gaps (of which I'm sure there are a few).  I recommend the entire Learning the Java Language trail, but the particularly relevant sections include the sections on Classes and Objects and Interfaces and Inheritance.

Access Modifiers and Special Considerations for Groovy

Access modifiers-- private, package-private (which is the default when no modifier is specified), protected, and public-- specify the context in which a class and its members can be read and/or modified in code.  In the context of the basic usage of an API (when using it in a soapUI script, for example, as opposed to more advanced usage like making your own modifications to the source code), you're generally only going to deal with classes and members marked public.  The two most restrictive levels of access, private and package-private are reserved for those classes and members that represent "internal wiring"-- strictly intended for use within a class (private) or package (package-private), not intended for direct use by API consumers.  In fact, the Javadoc tool excludes classes and members with these access modifiers by default, in which case they don't even appear in documentation.  Members at the protected level are only slightly less restricted in that they're inherited by sub-classes of their enclosing class (even sub-classes that aren't in the same package)-- for somebody using the API, this comes into play when creating your own sub-class, something beyond the basic scripting covered here.

However, there's a very important caveat when working with Groovy.  When trying some scripting in soapUI with the HSSFWorkbook class, I was surprised to see that I could in fact access the _sheets field.  If you look at the documentation, this field is marked protected, meaning it should only be accessible from within the class, within other resources in the same package, or from within a sub-class-- a Groovy Script step doesn't fall into any of those categories.  After investigating, I found this interesting post indicating that Groovy doesn't always respect access modifiers (see item 6)-- that's potentially a bad thing; there are good reasons why an API developer might restrict access to a certain member of a class, particularly when it comes to modifying its value.  As the article says, just because Groovy allows you to do something doesn't mean you should do it; you may have to police yourself a bit-- be wary of accessing classes or members marked anything other than public.

Static and Final

The final modifier generally restricts a user's ability to modify a class or member, although it means slightly different things in different contexts.  Applied to a class, it means the class can not be extended-- you can not create a sub-class based on that class.  A final method can not be overridden in a sub-class; in other words, you can't change the way the method is implemented, writing new code for it in your sub-class.  The value of a field marked final can not be changed.  As with the more restrictive access modifiers discussed above, the final modifier at the class and method level isn't something you'll need to worry about too much with basic usage of the API; it's more relevant when you start dealing with sub-classes.  With fields, however, more often than not you'll see them marked final in javadocs, and the distinction is important even in the context of basic scripting.

The static modifier designates a class (as opposed to instance) member.  A non-static member is associated with an individual object-- an instance of a class.  I discussed the _sheets field of the HSSFWorkbook class above-- if you were to create two different HSSFWorkbook objects and add sheets to each, the _sheets field would be different for each object; each instance of HSSFWorkbook would have its own value for the field.  A static member, on the other hand, is associated with the class itself; when you access a class member, you access it through the name of the class, not the name of a variable representing an instance of the class:

log.info(myWorkbook._sheets) //_sheets is accessed for an HSSFWorkbook instance named myWorkbook-- can be done in Groovy
log.info(HSSFWorkbook.INITIAL_CAPACITY) //INITIAL_CAPACITY is accessed via the HSSFWorkbook class itself

It's common to use the static and final modifiers together with fields to create constants-- so common, in fact, that there's a convention in Java to use all caps in these fields' names (as with the INITIAL_CAPACITY field above).  Class constants often represent values that are frequently used; creating class constants for these values eliminates the need to repeatedly declare variables to hold them.  Java's Math class has the static final field PI, for example; similarly, the Color class has static fields to represent frequently used colors: RED, BLUE, etc.  Again, there's no need to create objects of class Math or Color to access these constants; they're called against the class names themselves (Math.PI or Color.RED, for example).

Methods, too, can be marked static.  Returning to Java's Math class as an example, aside from the standard methods it inherits from the object class, all of its methods are static-- the class is essentially a toolbox of mathematical operations.  As with static fields, static methods are accessed via the class instead of an instance.

Method Return and Parameter Types

Method definitions in javadocs specify return types (the data types of results from functions) and parameter type(s) (the types of the values passed into the functions).  For the most part, these should be pretty straightforward; for example, look at the WsdlTestCase class in the API documentation for soapUI.  In the Method Summary table, find the getTestStepByName() method; in the left-hand (Modifier and Type) column you can see that the return type is WsdlTestStep.  In the Method and Description column, you can see in the parentheses next to the method name that the method takes a single String argument called stepName-- note that stepName is a somewhat arbitrary parameter name; any variable name could have been used when the method was created (of course, stepName is a good choice because it describes the significance of the String argument).  From the perspective of somebody using the method, the parameter name itself is relatively unimportant; what is important is recognizing the method takes a single String argument and returns a WsdlTestStep object.

The void keyword is used for the return type when a method doesn't return a value.  The setName() function of the WsdlTestCase class, for example, changes a property of the test case without actually returning anything.  For functions that don't take any arguments, the parentheses next to the method name are simply left empty.

Some functions return multiple data items; for these, the "enclosing" data type is also indicated.  For example, the getTestStepList() method of the WsdlTestCase class specifies a return type of List<TestStep>, meaning it returns an object of class List containing zero (the list could be empty) or more TestStep objects (more about what that means below).  The getTestSteps() method returns an object of class Map; Maps consist of key-value pairs, so the return type specifies Map<String,TestStep>-- each key-value pair in the returned Map consists of a String (the key) and a TestStep object (the value).

In some cases, a method's parameter and/or return types are determined at runtime, in which case you'll see a slightly complicated-looking syntax. Look at the getTestStepsOfType() method, for example.  The return type is given as <T extends TestStep> List<T>.  There's no T class, of course.  The T is used as a placeholder for a data type to be determined later.  The <T extends TestStep> part of the return type tells us that there's a restriction on T-- it has to be a type that's derived from the TestStep interface (a class that implements the TestStep interface, for example, or a class that extends another class that implements the interface, etc.), and the rest (List<T>) tells us that the method returns a List of objects of that type.  So how is the type actually determined? If you look at the method description column, getTestStepsOfType(Class<T> stepType) tells us that type T is the type of the class passed in for the stepType argument-- so the argument determines the meaning of T in the return type definition as well.

Return Types and Polymorphism

Above, when discussing the getTestStepList() method, I wrote that it returns a list of TestStep objects.  If you were to look at the TestStep definition in the javadocs, however, you'll see that TestStep is actually not a class-- it's an interface.  In my last post I mentioned that you can't actually create an instance of an interface.  What's going on here?  This is actually an illustration of the object-oriented concept of polymorphism-- it's a source of great flexibility and power in Java, but for me, at least, it was a source of some confusion when I first started working with javadocs.  In a very simplified nutshell, polymorphism means an object can be many things at the same time. Because an object can extend another class and/or implement multiple interfaces, it shares their characteristics and can be used wherever a parent class or any implemented interfaces are called for.  So for example, if an interface is specified as a method parameter or return type (as with the getTestStepList() method), it means that the method will take or return an object of a class that implements the given interface.  The same principle applies to classes that are sub-classed: if a class is given as a parameter or return type, the method can actually take or return an instance of the class or any sub-class of that class.  For example, WsdlTestStep is the return type for the getTestStepAt() method.  If you look at the javadoc entry for WsdlTestStep, you'll see that it has three sub-classes: WsdlPropertiesTestStep, WsdlRunTestCaseTestStep, and WsdlTestStepWithProperties (which itself has its own sub-classes).  The specific class returned by the method, then, can be any of the sub-classes of WsdlTestStep (including the sub-classes of the WsdlTestStepWithProperties class).

The problem with polymorphism in the context of javadocs is that they may not indicate the specific class that's ultimately returned by a call to the method in your script.  Knowing the class of the returned object is important, of course, in order to know what methods and properties are available to that object.  If push comes to shove, you can identify an object's class by using the getClass() method-- this is a basic method inherited by all classes from Java's Object class:
def myTestStep = testCase.getTestStepAt(1)
log.info(myTestStep.getClass())
//Example output: class com.eviware.soapui.impl.wsdl.teststeps.WsdlGroovyScriptTestStep
//The return type specified in the javadoc for getTestStepAt() is WsdlTestStep
//The object returned is actually an instance of an indirect sub-class of the WsdlTestStep class

Beginning soapUI Scripting: Javadocs 1

I've written quite a few posts now on scripting in soapUI; hopefully they've provided enough useful information to get you started on your own projects.  However, you may come to a point where you're trying to do something for which you can't find a good example, or you've found an example but need some help understanding it.  Or perhaps you're just curious to see what else might be possible with a script you've already written.  In this post, I want to begin a series looking at javadoc documentation to help you explore the capabilities of Java and Groovy libraries on your own.

The javadoc format is standardized, generated directly from a resource's code and specially formatted comments and annotation within the code (Javadoc is the name of the tool used to do this).  Although it may be a little non-intuitive at first, its ubiquity across the Java world makes even a basic understanding extremely useful: if you can read javadoc documentation, you can learn more not just about soapUI but many of the other APIs we've discussed in previous posts: POI, Groovy, and, of course, the base Java library itself.

Let's take a look at the HSSFWorkbook class of the Apache POI API, which I think provides a fairly straightforward example-- for those of you unfamiliar with it, this API allows you to work with Microsoft document formats within Java; the HSSFWorkbook class corresponds to an Excel xls workbook.  Launch the documentation page via this link (I recommend opening the link in a new window or tab so you can read this post and see the example side by side): http://poi.apache.org/apidocs/index.html.  Near the top of the page or the top of the main frame on the right (depending on your current view), you'll see links/buttons for "Frames" or "No Frames" that you can use to switch between two available views; this post is written from the perspective of the "Frames" view.

The top left frame lists the packages of the POI API, the organizational units into which its resources are divided.  You can select an individual package to view its classes or "All Classes" to view all of the API's classes together.  Scroll through the packages in the upper left frame and select the org.apache.poi.hssf.usermodel package, then take a look at the lower left frame, which should now be populated with the package's contents.  In this case, they're divided into three different categories: interfaces (I'll explain more about these a little later), classes, and enums.  These aren't the only categories you may encounter-- another common category is exceptions, for example. For the sake of this post and to keep things simple, I'm going to think in terms of two primary groups: classes and interfaces (enums and exceptions are both special types of classes).  Note that interfaces and classes are distinguished from each other by the way they're displayed; interfaces are always displayed in italics.

Find the HSSFWorkbook class in the lower left frame and click on it-- the main frame on the right will update and display information specific to the class.

Just above the name of the class at the top of the frame is its enclosing package.  In the context of scripting, one reason this is important is for import statements.  Recall from my post about using the XmlHolder class in soapUI that an import statement was necessary in order to create an instance of the class:

import com.eviware.soapui.support.XmlHolder

def myHolder = new XmlHolder(messageExchange.getResponseContentAsXml())

The fully qualified name of the class (including its package) is needed for the import statement.

The fully qualified name is also displayed in the class's hierarchical structure, displayed just below the name-- a description of the classes from which it inherits properties and methods and the interfaces implemented by the class.  I'll try to explain these concepts briefly here, but I strongly recommend checking out the more detailed explanation here in Oracle's Java Tutorials.

One benefit of Java's object-oriented approach is the idea of inheritance-- when creating a new class you don't have to start from scratch; you can designate another class as its base class and the new class starts with some (or all) of its base class's methods and properties.  You can then override some of those inherited members (create a new implementation of a method, for example) or add new ones to make the new class more specialized and add additional functionality.  In fact, there are some classes called abstract classes that are designed specifically to be used as base classes.  You can't actually create an instance of an abstract class; you'll never work with an abstract class directly in code.  However, "concrete" classes (which can be instantiated) can inherit from it.

Interfaces are similar to abstract classes in some ways.  In simplified terms, interfaces primarily define a set of methods-- but they don't provide any actual implementation of those methods.  The methods in an interface have names and signatures-- describing what types of data the methods take as arguments and what types are returned-- but they don't have any code for the methods.  Where does the actual code come from?  A class can implement an interface; if it does, it is required to "fill in" the implementation of each of the methods defined for the interface.  Importantly, a class can inherit from only one class (although that class may itself inherit from another, which may inherit from another, etc.), but it can implement multiple interfaces.

Returning to our example of the HSSFWorkbook class, its javadoc page shows us that it's a sub-class of (i.e., it inherits from) the POIDocument class and it implements the Workbook interface.

Just below the hierarchy information is the class's declaration-- this is actually how the class is declared in its underlying Java code:

public final class HSSFWorkbook
extends POIDocument
implements Workbook

I'll discuss the meaning of some of the keywords like public and final in the next post.  This declaration also tells us some of the same information we saw in the hierarchy information: the extends keyword indicates the class's base class (POIDocument), and the implements keyword indicates interfaces implemented by the class (Workbook).

Just below this information is a description of the class (the description is a single line here, but this may be much longer and more detailed in some cases) followed by the meat of the javadoc entry: descriptions of its members, its properties and methods.  These are described in brief summary form at the top of the page, then in more detail farther down on the page.

Fields are the member variables of a class-- variables associated with the class itself (as opposed to variables that are declared within methods or code blocks-- a variable used as a counter in a for loop would not be a member variable, for example).  For fields that are not inherited, the summary section shows them in a table with modifying keywords and type (the data type/class of the field) on the left and a name and description on the right.  Inherited fields are simply listed with the class or interface from which they're inherited, linked to their detailed descriptions in those classes and interfaces.

Below the field descriptions are the class's constructors.  Constructors are special methods used to create new instances of a class when used in conjunction with the new keyword:

//An import statement is required to use the constructor
import org.apache.poi.hssf.usermodel.HSSFWorkbook

def myXLSWorkbook = new HSSFWorkbook()

Note constructors have the same name as the class.  Also note that with the HSSFWorkbook there are multiple constructors, demonstrating the concept of overloading-- its possible for a class to have multiple methods (this is not limited to constructors) with the same name as long as each method takes different sets of argument types.  For example, you can see that one constructor takes no arguments, while another takes an InputStream as an argument, and yet another takes an InputStream and a boolean (true or false) as its arguments.  This is permissible because the data types of the arguments are unique for each constructor; the interpreter or compiler can tell which particular constructor you're trying to call based on the types of the arguments passed into the constructor.

Below the constructors are the remaining methods of the class.  These are displayed in a table with modifying keywords and type (the data type returned by the method) on the left and the method name and parameter types (the data types the method takes) on the right.  As with fields, methods that are inherited from other classes or interfaces are listed separately, linked to their detailed descriptions in the corresponding class or interface.

That completes a quick overview of a typical javadoc page.  In the next post, I'll look at some of those keyword modifiers and what they mean in the context of scripting.