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.