I need to compare dates in pipeline. And this is hilarious, the following code works ok in Groovy script console in Jenkins but not in a pipeline:
def created = new Date().parse("yyyyMMdd", "20191012")
def now = new Date().minus(30)
println created
println now
if (now > created) {
println "blah"
} else {
println "foo"
}
In the pipeline this gives me the following output:
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: java.util.Date.parse() is applicable for argument types: (java.lang.String, java.lang.String) values: [yyyyMMdd, 20191017]
Possible solutions: parse(java.lang.String, java.lang.String), parse(java.lang.String), parse(java.lang.String, java.lang.String, java.util.TimeZone), wait(), clone(), any()
I tried to change new Date().parse to Date.parse but then it exists with:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: No such static method found: staticMethod java.util.Date parse java.lang.String java.lang.String
How am I supposed to compare dates then?
EDIT: Rework using LocalDate
import java.time.LocalDate
import java.time.format.DateTimeFormatter
def due = 15
def creation_date = "20191012"
def dateFormat = DateTimeFormatter.ofPattern("yyyyMMdd")
def now = LocalDate.now().format(dateFormat);
def creation = LocalDate.parse(creation_date, dateFormat)
if (LocalDate.parse(now, dateFormat).minusDays(due) > creation) {
println "blah"
} else {
println "foo"
}
Works at Groovy console, does not work in pipeline, throws an error:
an exception which occurred:
in field org.jenkinsci.plugins.pipeline.modeldefinition.withscript.WithScriptScript.script
in object org.jenkinsci.plugins.pipeline.modeldefinition.agent.impl.LabelScript#55030b9c
in field groovy.lang.Closure.delegate
in object org.jenkinsci.plugins.workflow.cps.CpsClosure2#23e9719e
in field groovy.lang.Closure.delegate
in object org.jenkinsci.plugins.workflow.cps.CpsClosure2#165471ce
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.closures
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup#124c8a1d
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup#124c8a1d
Caused: java.io.NotSerializableException: java.time.format.DateTimeFormatter
I'm now totally puzzled how to deal with it...
EDIT2: SOLUTION
It appears that the code was pasted into pipeline without 'def' determiners. By trial and error I narrowed down that it was required to have at least def dateFormat other vars were not required to have that. Looks like all serializable variables need to be defined via 'def'.
I'm leaving that as it is so maybe sb will benefit from it.
Related
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¶m2=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¶m2=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¶m2=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¶m2=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.
Trying to read data from excel file and save it in Test suite properties, but getting this error (Using SOAP UI 5.1.3)
//This is the code
import java.io.*
import jxl.*
def file =new File("D:\\GroovyTest\\Example.xls")
def wb=Workbook.getWorkbook(file)
def sheet=wb.getSheet("Sheet1")
r=sheet.getRows()
for(int i=1;i<2;i++)
{
Cell c1=sheet.getCell(0,i)
testRunner.testCase.testSuite.addProperty("CityName"+i)
String cityName =c1.getContents()
testRunner.testCase.testSuite.setProperty("CityName"+i,cityName)
testRunner.runTestStepByName("GetSupplierByCity")
}
this is Error:
Error: groovy.lang.MissingMethodException: No signature of method: com.eviware.soapui.impl.wsdl.WsdlTestSuite.setProperty() is applicable for argument types: (java.lang.String, java.lang.String) values: [CityName1, New York] Possible solutions: getProperty(java.lang.String), addProperty(java.lang.String), hasProperty(java.lang.String), hasProperty(java.lang.String), getProject(), getProperties() error at line: 12
You are very close to get it.
As the error says, there is no such method setProperty.
In order to set either new property or modify existing property, use just setPropertyValue method.
So, all you need to do is simple. Remove below statement from your code.
testRunner.testCase.testSuite.addProperty("CityName"+i)
And change below statement
From:
testRunner.testCase.testSuite.setProperty("CityName"+i,cityName)
To:
testRunner.testCase.testSuite.setPropertyValue("CityName"+i,cityName)
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'
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.
I have created two groovy scripts as below. One script has a class which is instantiated in the other script. Both are in default package.
When I'm trying to run ImportGpsData.groovy I'm getting the following exception...
Caught: java.lang.ExceptionInInitializerError
java.lang.ExceptionInInitializerError
at ImportGpsData$_run_closure1.doCall(ImportGpsData.groovy:10)
at ImportGpsData.run(ImportGpsData.groovy:6)
Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
at DateParser.<clinit>(DateParser.groovy)
... 2 more
ImportGpsData.groovy
def file = new File('fells_loop.gpx')
def slurper = new XmlSlurper()
def gpx = slurper.parse(file)
gpx.rte.rtept.each {
println it.#lat
println it.#lon
def parser = new DateParser()
println parser.parse(it.time.toString())
}
Dateparser.groovy
#Grapes(
#Grab(group='joda-time', module='joda-time', version='2.3')
)
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
class DateParser {
def String parse(time){
def printableTime = new DateTime(time)
def format = DateTimeFormat.forPattern('MM/dd/yyyy - hh:mm aa')
return printableTime.toString(format)
}
}
I've found some other StackOverFlow questions that dealt with No Suitable classloader found for grab error. One answer suggested using #GrabConfig(systemClassLoader=true) inside #Grapes statement however adding it is resulting in compilation error, I'm getting error unexpected token # in line two.
#Grapes([
#Grab(group='joda-time', module='joda-time', version='2.3')
#GrabConfig( systemClassLoader=true )
])
Above way of using it gave unexpected token # found in line 3...
Adding a comma before #GrabConfig is giving the below error
Multiple markers at this line
- Groovy:Invalid duplicate class definition of class DateParser : The source F:\GroovyEclipses\src\DateParser.groovy contains at least two definitions of the class DateParser.
- General error during conversion: No suitable ClassLoader found for grab java.lang.RuntimeException: No suitable ClassLoader found for grab
After further analysis, I have figured that I'm getting this error when ever I user #Grapes and #Grab in any of my scripts. However I have to use them to work with joda-time.
Not sure if you were able to resolve this, if not then try to compile the class file first:
groovyc Dateparser.groovy
and then do
groovy ImportGpsData.groovy
should work.