SiKing

September 2, 2014

dynamic query parameters in SoapUI

Filed under: automation — SiKing @ 1:49 pm
Tags:

The other day I needed to create a REST call in SoapUI that had a parameter of the form multi[<multi_type>][<combins>]=<stake>. Everything in the trig-brackets is a variable. This is a perfectly valid URI and hence perfectly valid REST, but not very common and hence SoapUI has no clue how to handle this. After some discussions with SoapUI staff, the solution ended up being rather trivial.

You have to create a new event handler. This is a -Pro only feature, and if you are not sure how to set one up have a read through the documentation before continuing.

The event type that I used is SubmitListener.beforeSubmit. Apparently RequestFilter.filterRequest would do the job as well, but this event listener is broken in older versions of SoapUI … such as the one that I am still running. :/ In order to make things cleaner, I also set my target filter to .*multibet.*.
You have to get all three variables from somewhere. I chose to keep them in TestCase properties; at run time you can set them any way you chose, such as property transfer.
The event code ends up being:

def multi_type = context.expand('${#TestCase#multi_type}')
def combins = context.expand('${#TestCase#combins}')
def stake = context.expand('${#TestCase#stake}')
def multi = submit.request.addProperty("multi[$multi_type][$combins]")
submit.request.setPropertyValue(multi.name, stake)

Note that this will actually modify the method in your service endpoint, which could mess up any other calls in your test suite. To clean this up, create a second event, of type SubmitListener.afterSubmit, probably same target if you are using one, with the code:

def multi_type = context.expand('${#TestCase#multi_type}')
def combins = context.expand('${#TestCase#combins}')
submit.request.removeProperty("multi[$multi_type][$combins]")

Simple, right? But wait, it gets better! :mrgreen:

I thought this same approach could be used to solve a problem SoapUI has had like for ever – and yes SmartBear, this is a problem! Array parameters; the problem has been discussed in various places.

At first I tried some kind of loop, where I did multiple submit.request.addProperty() each followed by submit.request.setPropertyValue(), but only the last one actually took. This proved that SoapUI does not support this at the object level (and not just at the GUI level), in which case the enhancement from them is probably not going to be trivial.

Again start by creating a SubmitListener.beforeSubmit. The toughest part here is deciding how you are going to mark the parameter and pass the values that need to be processed. There are two methods in the API that give you access to all the call parameters: submit.request.getPropertyList() and submit.request.getProperties(); each of these is broken in different ways in different older versions of SoapUI. 😕

Some options that I experimented with are:

  • Pass the values looking like an array, something like [1, 2, 3].
    submit.request.getPropertyList().each {
    
    	// extract the Array parameter name and array of values
    	def arrParamName
    	def arrParamValues = []
    	if(it.value.contains('[')) {
    		arrParamName = it.name
    		arrParamValues = Eval.me(it.value)	// converts a String to an ArrayList
    	}
    	// wasn't an array
    	if(arrParamName == null)
    		return
    
    	// convert the array of values into multiple name-value pairs ... discussed below
    }
    

    This has the problem what if you want to pass the literal string [1, 2, 3]? Also, it seems too verbose.

  • Somehow specially marking the parameter and still pass the values looking like an array. When you define your method and its parameters, there are several things that you can define about it. In order for any of this to work you need to set Disable Encoding. Have a look at the reference; we are talking about control number 13.
    submit.request.getProperties().each {
    
    	if(!it.value.isDisableUrlEncoding())
    		return
    
    	// convert the array of values into multiple name-value pairs ... discussed below
    }
    
  • Alternatively, set the Type (control number 10) to something unique.
    submit.request.getProperties().each {
    
    	if(!it.value.type.equals('{http://www.w3.org/2001/XMLSchema}anyType'))
    		return
    
    	// convert the array of values into multiple name-value pairs ... discussed below
    }
    

Now to convert the array of values into multiple parameters. So if your parameter name-value looks like foo=[1, 2, 3] you want to end up with foo=1&foo=2&foo=3. We already have the first "foo=", we just need to create the rest of this:

def arrParamName = it.key
def arrParamValues = Eval.me(it.value.value)	// yes, .value twice!
def nvPairs = new StringBuilder()
nvPairs << arrParamValues.remove(0)
arrParamValues.each {
	nvPairs << '&'
	nvPairs << arrParamName
	nvPairs << '='
	nvPairs << it
}

Now you need to pass this string undecoded to the original parameter.

submit.request.getProperty(arrParamName).setDisableUrlEncoding(true)
submit.request.setPropertyValue(arrParamName, nvPairs.toString())

If the parameter is already set to Disable Encoding, then the first line above is not needed.

This solution is not perfect, but hopefully it is enough to at least get you going.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: