How to programmatically build a ForEach input? - groovy

I have written a custom JSR223 PostProcessor in order deduplicate a JSON Extractor array, and then build all of the needed variables for a ForEach Controller
import java.util.stream.Collectors;
def previousValuesMatchNrAsString = vars.get("distinctServiceIds_matchNr");
def previousValuesMatchNr = previousValuesMatchNrAsString ? previousValuesMatchNrAsString.toInteger() : 0;
for(i = 1; i <= previousValuesMatchNr; i++) {
vars.remove("distinctServiceIds_" + i);
}
vars.remove("distinctServiceIds_ALL");
vars.remove("distinctServiceIds_matchNr");
def values = vars.get("serviceIds_ALL").split(",");
def newValues = Arrays.stream(values).distinct().collect(Collectors.toList());
def newValuesCount = newValues.size();
def joinesNewValues = String.join(",", newValues);
vars.put("distinctServiceIds_ALL", joinesNewValues);
newValues.eachWithIndex { var, idx -> vars.put("distinctServiceIds_" + (idx + 1), var) };
vars.put("distinctServiceIds_matchNr", newValuesCount.toString());
I have to cleanup variables first, because this JSR223 PostProcessor runs into another ForEach Controller, and then I have to populate distinctServiceIds_ALL, distinctServiceIds_matchNr and all of the indexed variables in order to use distinctServiceIds as "Input variable prefix" for my ForEach Controller.
Although this works, it seems very hacky to me and I wonder if there's some Groovy function, or something else, to do all of that work.

there are many additional methods defined in groovy
as soon as vars contains type of JMeterVariables to remove old vars you could collect required entries from iterator and call remove for each one
the extension groovy methods for iterator could be found in groovy docs
vars.getIterator().findAll{ it.getKey().startsWith('distinctServiceIds_') }.each{
vars.remove(it.getKey())
}
because of groovy it could be simplified to this:
vars.iterator.findAll{ it.key.startsWith('distinctServiceIds_') }.each{
vars.remove(it.key)
}
the same with other generic types: List, Maps, etc
collecting unique values and transforming to map:
def values = [111,222,333,444,444,444,444]
def valMap = values.unique().indexed().collectEntries{k,v-> ['distinctServiceIds_'+(k+1),v] }
vars.putAll(valMap)
so, check groovy jdk extension documentation

Related

How to use map in groovy

Here is how my script currently looks like -
baseList = readFile('text2.txt').split('\n') as List
def cleanList = []
for (i=0; i < baseList.size; i++) {
if (baseList[i].contains('=')){
cleanList += baseList[i]
}
}
print(cleanList)
This gives following output-
[Pipeline] echo
[version.build=874, version.maintenance=0, version.major=1, version.minor=37]
I want these values to go into another variable called "svnTag"
def svnTag="ccsmp_v_${version.major} ${version.minor} ${version.maintenance} ${version.build}"
So that when I print svnTag, it output something like this-
SVN_TAG=ccsmp_v_1.37.0.846
You are not using a Map, but a List of String, where each element is in the form of <key>=<value>
If you want to parse your file to a Map, you could use something like:
def baseList = readFile('text2.txt').split('\n') as List
def map = [:]
for (el in baseList) {
if (el.contains('=')) {
def parts = el.split('=')
map[parts[0]] = parts[1]
}
}
Then you can use the values from the map with:
def svnTag="ccsmp_v_${map['version.major']} ${map['version.minor']} ${map['version.maintenance']} ${map['version.build']}"
If your file is a valid property file (all lines are in form of key=value), you can use the readProperties step that will create a Properties object for you (that is in fact a Map):
def map = readProperties file: 'text2.txt'

What is the meaning of this script field in Groovy?

What parameters.script means in the following Groovy code:
example.groovy
class Example {
def call(Map parameters) {
def script = parameters.script
...
}
}
It retrieves a value from a Map with the key 'script'. The following are all equivalent
def script = parameters.script
script = parameters['script']
script = parameters.get('script')
from https://www.timroes.de/2015/06/28/groovy-tutorial-for-java-developers-part3-collections/:
Beside using brackets you can also use dot notation to access entries

How to make snakeyaml and GStrings work together

When I'm trying to use snakeyaml to dump out Yaml out of Groovy interpolated strings, it ends up printing a class name instead.
For example:
#Grab(group='org.yaml', module='snakeyaml', version='1.16')
import org.yaml.snakeyaml.Yaml
Yaml yaml = new Yaml();
def a = "a"
def list = ["$a"]
def s = yaml.dump(list)
Prints:
- !!org.codehaus.groovy.runtime.GStringImpl
metaClass: !!groovy.lang.MetaClassImpl {}
I'm guessing it has something to do with the fact that GStrings get transformed to Strings when they used and I suspect snakeyaml uses some sort of introspection to determine the class of the object.
Is there a better solution than calling toString() on all GStrings?
Try to create a new Representer :
public class GroovyRepresenter extends Representer {
public GroovyRepresenter() {
this.representers.put(GString.class, new StringRepresenter());
}
}
Yaml yaml = new Yaml(new GroovyRepresenter())
...
You could add type info to your variables
Yaml yaml = new Yaml();
def a = "a"
String aStr = "$a"
def list = [aStr]
def s = yaml.dump(list)

soapUI: How to set lists as property

I want to do the following in soapUI using Groovy:
Select random value and correspondating data from database
Get the response from webservice
Compare my database data with the response from the webservice
To achieve this I have the following steps definied:
Created property object for the test suite
Groovy Script to get the random and correspondating values from database
Transformation to put the random value into the request
Webservice call to get response
Groovy Script to compare the values from my database with the response of the webservice
I did the same steps for other tests already by putting the values from database into the property object in step 2 and reading them from there in step 5. But up to now the values were always normal String or int. In the response I get in my current test case I get a list back. So I create an array and filled it with bean objects my wanted data in step 2. In step 5 I parse my XML-String and convert the data into an Object-Array too. So I can compare all attributes of them.
I wrote the whole test case in a singular script and tested it on groovy console first. When I started to transform it to soapUI and working with the property to "transport" the data from step 2 to step 5 my problem occurs as it looks like I can't put an Arraylist to the properties (see error message below).
Now I'm confused that this is not possible as I can easily put SQL-Instances in the properties:
def contextSqlInstanz = Sql.newInstance(urlH2, userH2, passwordH2, driverH2)
context.setProperty( "contextSqlInstanz", contextSqlInstanz )
sql = context.getProperty("contextSqlInstanz");
So how I can transport my Array, filled the Objects, from step 2 to step 5 to compare it with my webservice response. I DON'T want to convert both to strings and compare if the strings are equal but I want to compare each attributes of my bean-class by hand.
Bean Class:
class myBean {
String value1;
String value2;
...
}
Reading my local database, generating the beans and put them into a list
function getdata() {
def liste = []
// sql-statements
sql.eachRow(...) {
def myBean = new myBean();
myBean.value1 = it.value1.toString();
myBean.value2 = it.value2.toString();
...
liste.add(Schluesselwert)
}
return liste
}
Trying to put the list into properties
sollListeH2 = getdata()
def props = testRunner.testCase.getTestStepByName("P_testcase")
props.setPropertyValue( "sollListe", sollListeH2)
results in:
groovy.lang.MissingMethodException: No signature of method: com.eviware.soapui.impl.wsdl.teststeps.WsdlPropertiesTestStep.setPropertyValue() is applicable for argument types: (java.lang.String, java.util.ArrayList) values: [sollListe, [value1#15d4334, value2#1e739c8, ...]] Possible solutions: setPropertyValue(java.lang.String, java.lang.String), getPropertyValue(java.lang.String) error at line: 91
As I didn't find another way I did it the ugly way by putting each value as an own property into the propteries
Setting the props in step 2:
def convertVectorToProps(vector) {
def size = vector.size();
def props = testRunner.testCase.getTestStepByName("P_testcase")
props.setPropertyValue("sollSize", size.toString())
for(int i=0; i < size; i++) {
props.setPropertyValue("myBean.value1" + i, vector.value1[i]);
props.setPropertyValue("myBean.value2" + i, vector.value2[i]);
...
}
}
Reading the props in step 5 and build new vector:
def convertPropsToVector() {
def props = testRunner.testCase.getTestStepByName("P_testcase")
def sollSize = props.getPropertyValue("sollSize").toInteger()
SollListe = [];
for(int i=0; i < sollSize; i++) {
def myBean = new myBean();
myBean.value1 = props.getPropertyValue("myBean.value1" + i);
myBean.value2 = props.getPropertyValue("myBean.value2" + i);
SollListe << myBean
}
return SollListe
}

Groovy Dynamic arguments

I wonder how is this possible in groovy to start an array from the n element.
Look at the snippet :
static void main(args){
if (args.length < 2){
println "Not enough parameters"
return;
}
def tools = new BoTools(args[0])
def action = args[1]
tools."$action"(*args)
System.exit(1)
}
As you see am doing here a dynamic method invocation. The first 2 arguments are taken as some config and method name , the others I would like to use as method paramerts.
So how can I do something like this :
tools."$action"(*(args+2))
Edited : If not possilbe in native groovy Java syntax will do it :
def newArgs = Arrays.copyOfRange(args,2,args.length);
tools."$action"(*newArgs)
To remove items from the beginning of the args you can use the drop() method. The original args list is not changed:
tools."$action"(*args.drop(2))
Other option, like you are trying is to access from N element:
tools."$action"(*args[2..-1])

Resources