How to access a variable outside of a loop in Groovy - groovy

Beginner alert! I have a simple Groovy script that aims to break an argument list into key-value pairs, stored in an associative array (HashMap?). The code works fine until the point where it splits the parameters, but when it tries to put the results back into the array, it throws an exception, stating it cannot access a null element.
I suppose the reason for this is that it can't access the variable that was declared outside the loop.
Here's the script:
def input = "https://weyland-yutani.corp/engineering/bio?param1=1&param2=2"
def params = [:] // wanna store key-value pairs here
if (input.split('\\?').size() >= 2) {
def p = input.split('\\?').last() // get the param string
p.split('\\&').each { // cut the string into an argument list
def keyval = it.split('=') // cut the argument into a key-value pair
println keyval // <-- prints "[param1, 1]", looks okay
params[keyval[0]].put(keyval[1]) // ERROR: "Cannot invoke method put() on null object"
//params[keyval[0]].add(keyval[1]) // ERROR, same sh**
}
}
Error message:
Caught: java.lang.NullPointerException: Cannot invoke method put() on null object
java.lang.NullPointerException: Cannot invoke method put() on null object
at jdoodle$_run_closure1.doCall(jdoodle.groovy:10)
[...]
As it was stated in this article, the way you declare a variable can affect it's scope, but none of my tries succeeded.
Could you give me an advice what am I missing?

The following code:
def input = "https://weyland-yutani.corp/engineering/bio?param1=1&param2=2"
def params = input.tokenize('?').last().tokenize('&').collectEntries { keyval ->
keyval.tokenize('=')
}
println params.getClass()
println params
demonstrates one way of doing this. When run, this prints:
─➤ groovy solution.groovy
class java.util.LinkedHashMap
[param1:1, param2:2]
─➤
As an alternative, if you are ok with using an external library, you could use a url parsing class. This example from HttpBuilder (which is a tad outdated at this point, there are probably others out there):
#Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.7.2')
import groovyx.net.http.URIBuilder
def input = "https://weyland-yutani.corp/engineering/bio?param1=1&param2=2"
def uri = new URIBuilder(input)
println uri.query.getClass()
println uri.query
which, when run, prints:
─➤ groovy solution2.groovy
class java.util.HashMap
[param1:1, param2:2]
─➤
As another example, this time using google http client library class GenericUrl:
#Grab('com.google.http-client:google-http-client:1.39.1')
import com.google.api.client.http.GenericUrl
def input = "https://weyland-yutani.corp/engineering/bio?param1=1&param2=2"
def url = new GenericUrl(input)
println (url instanceof Map)
url.each { k, v ->
println "$k -> $v"
}
println "value of param1: ${url.param1}"
which, when executed prints:
─➤ groovy solution3.groovy
true
param1 -> [1]
param2 -> [2]
value of param1: [1]
─➤
It should be noted that google does the right thing here. When asking for the value of a parameter, the answer should really be a list. This is because you can say ?a=1&a=2&a=3 which from what I understand should be interpreted not as replacing the value of a, but rather as a being a list of values 1, 2, 3.
So when you ask the google library for the value of a param, you get back a list which in this case happens to be one param long.

There is no code that is ever putting anything in params so when you do params[keyval[0]] that will always evaluate to null, so params[keyval[0]].put(keyval[1]) can't work because you are invoking .put on a null reference.

Related

How to pass Map between testCases using properties

I want to do the following in SOAPUI using Groovy:
In a TestCase1 select values (Lastname, firstname) from database, and create a Map with dynamic values: def Map = [Login :"$Login", Nom: "$Nom"]
I need my map to be transferred to another TestCase, for this
I'm trying to put my map into properties:
testRunner.testCase.setPropertyValue( "Map", Map)
But I have error:
groovy.lang.MissingMethodException: No signature of method:
com.eviware.soapui.impl.wsdl.WsdlTestCasePro.setPropertyValue() is
applicable for argument types: (java.lang.String,
java.util.LinkedHashMap) values: [OuvInfoPersoMap,
[Login:dupond0001, Nom:Dupond]] Possible solutions:
setPropertyValue(java.lang.String, java.lang.String),
getPropertyValue(java.lang.String) error at line: 123
I found some posts on internet that suggests to use metaClass groovy property
context.testCase.metaClass.map = Map
log.info context.testCase.map
But I don't think it enough in my case.
I would like to be able to pass a map to Testcase2 using:
createMap = testRunner.testCase.testSuite.project.testSuites.testCases["TestCase1"]
createMap.map
Hopefully you can help me solving this problem.
Thanks advance
As #SiKing correctly explain in the comments, setPropertyValue method expects and String for the property name and for the property value.
Note that as #Rao suggest in general testCase execution should be independent, however despiste this technically it's possible to do what you ask for.
So a possible solution for your case is in the first testCase to serialize the Map to String in order that it's possible to save using setPropertyValue(Strig propertyName, String value) method, and then in the second testCase deserialitze it, something like the follow code must work:
TestCase 1
Serialize the map using inspect() method and save it as a property:
def map = ['foo':'foo','bar':'bar', 'baz':'baz']
testRunner.testCase.setPropertyValue('map',map.inspect())
TestCase2
Deserialitze the String property using Eval.me(String exp)::
// get the first testCase
def testCase1 = testRunner.testCase.testSuite.testCases["TestCase1"]
// get the property
def mapAsStr = testCase1.getPropertyValue('map')
// deserialize the string as map
def map = Eval.me(mapAsStr)
assert map.foo == 'foo'
assert map.bar == 'bar'
assert map.baz == 'baz'

Groovy: Different behaviour observed using the eachWithIndex method

I was doing a Groovy tutorial online there and after playing around with the code I observed some behaviour that I can't understand.
First I created a Map object like this:
def devMap = [:]
devMap = ['name':'Frankie', 'framework':'Grails', 'language':'Groovy']
devMap.put('lastName','Hollywood')
Then I called eachWithIndex to print out the values like so:
devMap.eachWithIndex { println "$it.key: $it.value"}
Which printed this to the console:
name: Frankie
framework: Grails
language: Groovy
lastName: Hollywood
But when I printed to the console from the eachWithIndex method like this using the arrow operator:
devMap.eachWithIndex { it, i -> println "$i: $it" }
The following got printed to the console:
0: name=Frankie
1: framework=Grails
2: language=Groovy
3: lastName=Hollywood
So what I can't understand is why the indexes got printed with the second statement and why there are = signs but no : signs between the key-value pairs?
Thanks.
When you use the no-arg version of eachWithIndex, it is the current entry in the Map. That means that it.key and it.value return what you expect.
When you use the two-arg version of eachWithIndex, again, it is the current entry in the Map and i is the current index. You're printing i, the index, and then since you are only printing it, you are getting the result of it.toString(), which formats the map entry as "${it.key}=${it.value}"
Your second example is equivalent to:
devMap.eachWithIndex { it, index -> println "$index: ${it.toString()}" }
where this shows that the toString() implementation uses the = syntax:
devMap.each { println it.toString() }
Note that this is closer to your goal (as I interpret it):
devMap.eachWithIndex { it, index -> println "$index: ${it.key}: ${it.value}" }

Is there a way to call a class method inside an eval?

I'm writing Groovy scripts that are pasted into a web-based system to be run. There is a
class available to scripts run in this environment which I'll call BrokenClass. It has a
bug where it will only accept a string literal as its first parameter, but not a variable
with a string in it. So, this will work (it returns a list):
BrokenClass.reflist('something', 'name')
However, if I try to use a variable as the first parameter I get an error:
list_name = 'something'
BrokenClass.reflist(list_name, 'name')
This produces the message Metadata RefList[something] cannot be accessed.
I don't have any control over BrokenClass (aside from filing a bug on it). I tried to work
around the problem with something like this:
list_name = "foo"
list_call = "BrokenClass.reflist(${list_name}, 'name')"
list_values = Eval.me(list_call)
However, that produces an error:
groovy.lang.MissingPropertyException: No such property: BrokenClass for class: Script1
I tried adding an import to my string, but then I get unable to resolve class BrokenClass.
Is there a way to use BrokenClass inside the eval'd string? Or some other way I haven't
considered to work around the bug in BrokenClass.reflist? A really long switch block
is out, because the possible list names change.
The method signature for BrokenClass.reflist is:
public static List<Object> reflist(String reflistName, String field);
I have a suspicion that BrokenClass.reflist() is directly or indirectly doing an improper String comparison by using the == operator rather than String.equals(). See this article for an explanation of the difference.
The problem
Here's a demonstration of the problem:
def a = 'whatever'
def b = 'what' + 'ever'
assert doSomething('whatever') == 'OK'
assert doSomething(a) == 'OK'
assert doSomething(b) == 'ERROR'
def doSomething(String value) {
if(value.is('whatever')) { // In Java this would be: value == "whatever"
'OK'
} else {
'ERROR'
}
}
Because it's using reference equality, which in Groovy is done by the Object.is(Object) method, BrokenClass.reflist() was inadvertently coded to work only with String literals: all String literals with the same value refer to the same String instance, resulting in an evaluation of True. A String composed at run time with the same value of a literal does not refer to the same String instance.
Work around
Obviously BrokenClass.reflist() should be fixed. But you can work around the problem by using an interned String.
def b = 'what' + 'ever'
assert doSomething(b.intern()) == 'OK'
def doSomething(String value) {
if(value.is('whatever')) {
'OK'
} else {
'ERROR'
}
}
If the variable's value matches that of a String literal, then variable.intern() will return the same String instance as the matching literal. This would allow the Java == operator to work as you need it to.

how to use testrunner.fail in groovy class for soapui

I am trying to fail the test case if string values are not same.I have created a class and a method to compare the string values.
public class test1 {
public String onetwo(str1,str2)
{
def first = str1
def second=str2
if (first==second)
{
return "Strings are same"
}
else
{
testrunner.fail("String Values are not same")
}
}
public String fail(reason)
{
return "implementation of method1"+reason
}
}
def objone =new test1()
def result = objone.onetwo('Soapui','Soapui')
def result1 = objone.onetwo('Soapui','SoapuiPro')
while executing it i am getting below message for last line of above code
ERROR:groovy.lang.MissingPropertyException: No such property: testrunner for class: test1
Please suggest how to use testrunner.fail or any other way to fail the test case if strings are not same.
Thank You
It can't find SoapUI's testrunner because you're accessing it inside your own class. (Note that the error message is trying to find test1.testrunner, which of course does not exist.) If you accessed testrunner from the top level of the script (where you define your variables) it should work.
If you still want to have a reusable class/method, an easy fix would be to have it return a boolean or an error message, and then you call testrunner.fail if your method returns false/error. Something like this (although a boolean return might make for cleaner code):
public class test1 {
public String onetwo(str1,str2)
{
def first = str1
def second=str2
if (first==second)
{
return "Strings are same"
}
else
{
return "String Values are not same"
}
}
...
}
def objone =new test1()
def result = objone.onetwo('Soapui','Soapui')
def result1 = objone.onetwo('Soapui','SoapuiPro')
if (result != "Strings are same")
{
testrunner.fail(result)
}
...
This thread from another site also describes a much more complex solution of making reusable Groovy libraries for SoapUI.
If two strings are to be compared in groovy script test step, then it is not required to write a class, instead can be achieved with below statement.
Matching Samples - note that nothing happens on successful assertion
String compare and fail if not equal
assert 'Soapui' == 'Soapui', "Actual and expected does not match"
Another example - with boolen
def result = true;assert result, "Result is false"
Non-Matching example - test fails on failure
Strings not matching, and shows error message
assert 'Soapui' == 'SoapuiPro', "Actual and expected does not match"
Another example with non zero test
def number=0;assert number, "Number is zero"
If you need only example of the class and like to access testRunner object, then we need pass it either to the class or to the method where testRunner is needed. Otherwise, other class do not know about the objects that are available to groovy script.
Here is some more information on the objects availability at various level of test case hierarchy.
When soapUI is started, it initializes certain variables and are available at Project, Suite, Test case, Setup Script, Teardown script etc., If you open the script editor you would see the objects available there.
For instance, groovy script test step has log, context, testRunner, testCase objects. However, if some one creates a class with in Groovy Script test step, those objects are not available with in that user defined class.

Test Groovy class that uses System.console()

I have a groovy script that asks some questions from the user via a java.io.console object using the readline method. In addition, I use it to ask for a password (for setting up HTTPS). How might I use Spock to Unit Test this code? Currently it complains that the object is a Java final object and can not be tested. Obviously, I'm not the first one trying this, so thought I would ask.
A sketch of the code would look something like:
class MyClass {
def cons
MyClass() {
cons = System.console()
}
def getInput = { prompt, defValue ->
def input = (cons.readLine(prompt).trim()?:defValue)?.toString()
def inputTest = input?.toLowerCase()
input
}
}
I would like Unit Tests to test that some mock response can be returned and that the default value can be returned. Note: this is simplified so I can figure out how to do the Unit Tests, there is more code in the getInput method that needs to be tested too, but once I clear this hurdle that should be no problem.
EDITED PER ANSWER BY akhikhl
Following the suggestion, I made a simple interface:
interface TestConsole {
String readLine(String fmt, Object ... args)
String readLine()
char[] readPassword(String fmt, Object ... args)
char[] readPassword()
}
Then I tried a test like this:
def "Verify get input method mocking works"() {
def consoleMock = GroovyMock(TestConsole)
1 * consoleMock.readLine(_) >> 'validResponse'
inputMethods = new MyClass()
inputMethods.cons = consoleMock
when:
def testResult = inputMethods.getInput('testPrompt', 'testDefaultValue')
then:
testResult == 'validResponse'
}
I opted to not alter the constructor as I don't like having to alter my actual code just to test it. Fortunately, Groovy let me define the console with just a 'def' so what I did worked fine.
The problem is that the above does not work!!! I can't resist - this is NOT LOGICAL! Spock gets 'Lost' in GroovyMockMetaClass somewhere. If I change one line in the code and one line in the test it works.
Code change:
From:
def input = (cons.readLine(prompt).trim()?:defValue)?.toString()
To: (add the null param)
def input = (cons.readLine(prompt, null).trim()?:defValue)?.toString()
Test change:
From:
1 * consoleMock.readLine(_) >> 'validResponse'
To: (again, add a null param)
1 * consoleMock.readLine(_, null) >> 'validResponse'
Then the test finally works. Is this a bug in Spock or am I just out in left field? I don't mind needing to do whatever might be required in the test harness, but having to modify the code to make this work is really, really bad.
You are right: since Console class is final, it could not be extended. So, the solution should go in another direction:
Create new class MockConsole, not inherited from Console, but having the same methods.
Change the constructor of MyClass this way:
MyClass(cons = null) {
this.cons = cons ?: System.console()
}
Instantiate MockConsole in spock test and pass it to MyClass constructor.
update-201312272156
I played with spock a little bit. The problem with mocking "readLine(String fmt, Object ... args)" seems to be specific to varargs (or to last arg being a list, which is the same to groovy). I managed to reduce a problem to the following scenario:
Define an interface:
interface IConsole {
String readLine(String fmt, Object ... args)
}
Define test:
class TestInputMethods extends Specification {
def 'test console input'() {
setup:
def consoleMock = GroovyMock(IConsole)
1 * consoleMock.readLine(_) >> 'validResponse'
when:
// here we get exception "wrong number of arguments":
def testResult = consoleMock.readLine('testPrompt')
then:
testResult == 'validResponse'
}
}
this variant of test fails with exception "wrong number of arguments". Particularly, spock thinks that readLine accepts 2 arguments and ignores the fact that second argument is vararg. Proof: if we remove "Object ... args" from IConsole.readLine, the test completes successfully.
Here is Workaround for this (hopefully temporary) problem: change the call to readLine to:
def testResult = consoleMock.readLine('testPrompt', [] as Object[])
then test completes successfully.
I also tried the same code against spock 1.0-groovy-2.0-SNAPSHOT - the problem is the same.
update-201312280030
The problem with varargs is solved! Many thanks to #charlesg, who answered my related question at: Spock: mock a method with varargs
The solution is the following: replace GroovyMock with Mock, then varargs are properly interpreted.

Resources