I'm getting a text which contains ${somethingElse} inside, but it's just a normal String.
I've got a class:
class Whatever {
def somethingElse = 5
void action(String sth) {
def test = []
test.testing = sth
assert test.testing == 5
}
}
Is it possible with groovy?
EDIT:
my scenario is: load xml file, which contains nodes with values pointing to some other values in my application. So let's say I've got shell.setVariable("current", myClass). And now, in my xml I want to be able to put ${current.someField} as a value.
The trouble is, that the value from xml is a string and I can't evaluate it easily.
I can't predict how these "values" will be created by user, I just give them ability to use few classes.
I cannot convert it when the xml file is loaded, it has to be "on demand", since I use it in specific cases and I want them to be able to use values at that moment in time, and not when xml file is loaded.
Any tips?
One thing you could do is:
class Whatever {
def somethingElse = 5
void action( String sth ) {
def result = new groovy.text.GStringTemplateEngine().with {
createTemplate( sth ).make( this.properties ).toString()
}
assert result == "Number 5"
}
}
// Pass it a String
new Whatever().action( 'Number ${somethingElse}' )
At first, what we did, was used this format in xml:
normalText#codeToExecuteOrEvaluate#normalText
and used replace closure to regexp and groovyShell.evaluate() the code.
Insane. It took a lot of time and a lot of memory.
In the end we changed the format to the original one and crated scripts for each string we wanted to be able to evaluate:
Script script = shell.parse("\""+stringToParse+"\"")
where
stringToParse = "Hello world # ${System.currentTimeMillis()}!!"
Then we were able to call script.run() as many times as we wanted and everything performed well.
It actually still does.
Related
I have a spreadsheet where each row contains a SOAP request, concatenated from the data in that row. So cell G2 is a SOAP request, cell G3 is a different SOAP request, cell G4 is another SOAP request and so on. I've looked to see what is the best method for transferring those 100+ SOAP requests into SoapUI (free version) and running them all in one batch. I've not found anything that gives me the full working solution yet. Could someone suggest the best method please? Thanks in advance!
I won't tell you how to do it, but I can tell you how I did it. Concatenating XML in Excel is messy and error-prone, especialy if you have less-technical people writing the test cases. I asked for the data (parameters) in Excel.
I wrote a Groovy script that gets the data (there's a nice script that abstracts it for you), and for each test case, saved the values as properties:
new ExcelBuilder("data/MyRegressionSheetV1.2.xls").eachLine([labels:true]) { row ->
Map payLoad = [:]
if (cell(0)) {
payLoad['MyName'] = cell(0)
payLoad['MySurname'] = cell(1)
//and so on
payLoad['ExpectedResult'] = cell(12)
testRunner.testCase.testSuite.project.setPropertyValue("Name", payLoad['MyName'])
testRunner.testCase.testSuite.project.setPropertyValue("Surname", payLoad['MySurname'])
//and so on
def MyScenario = testRunner.testCase.testSuite.testCases["SOAP Request1"].run( null, true )
then made the SOAP requests with parameterised values:
<parm:Name>${#Project#Name}</parm:Name>
<parm:Surname>${#Project#Surname}</parm:Surname>
//and so on
While in the loop, you can get the response values and if you have expected results, you can compare them programmatically:
def responseSOAP = context.expand('${SOAP Request1#Response}')
def responseSection = responseSOAP =~ /requestAnswer>(.*)<\/requestAnswer/
def response = responseSection[0][1]
if (response.equals(payLoad['ExpectedResult'])) {
testResult = 'Pass'
}
}
Opensource SoapUI is very capable, and Groovy is a very cool programming language. A lot is possible once you get your head around how they work together.
EDIT:
If you want to replace the entire request, you can access it with the XmlHolder:
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context )
//this gets a handle on the current request XML
def oldHolder = groovyUtils.getXmlHolder( 'SOAP Request1#Request' )
//your String XML from the spreadsheet must be parsed into XML
def newHolder = new XmlSlurper().parseText(MyXMLFromSheetAsString)
//replace
context.requestContent = newHolder.xml
Compare two text files using groovy in Jenkins slave machine. Sample text files are as follows.
Sample1.txt:
team_a, added
team_b, removed
team_c, added
Sample2.txt:
team_d, added
team_e, added
team_c, removed
Need to identify the change and should give the output as team_c has been removed.
Asking this question as being newbie to groovy.
You haven't posted your code, but say you are a newbie, so I'll talk you through an example:
// For simplicity, I won't use files, but a "heredoc"
Sample1txt = '''
team_a, added
team_b, removed
team_c, added
'''
Sample2txt ='''
team_d, added
team_e, added
team_c, removed
'''
//get the data in a map each
Map a = [:]
Sample1txt.splitEachLine(",") { line -> a[line[0]] = line[1]}
Map b = [:]
Sample2txt.splitEachLine(",") { line -> b[line[0]] = line[1]}
//for each key value pair in the second map...
b.each {k, v ->
//if the key exists (you didn't say what you want to do with new keys)
if (a.containsKey(k)) {
//and if the key's value is not equal to the same key's value in the first map
if (!b[k].equals(a[k])) {
//print it
println "${k} : ${v}"
}
}
}
This is not a robust solution, but it will point you in the right direction.
I would like to know if it is possible to retrieve the name of a variable.
For example if I have a method:
def printSomething(def something){
//instead of having the literal String something, I want to be able to use the name of the variable that was passed
println('something is: ' + something)
}
If I call this method as follows:
def ordinary = 58
printSomething(ordinary)
I want to get:
ordinary is 58
On the other hand if I call this method like this:
def extraOrdinary = 67
printSomething(extraOrdinary)
I want to get:
extraOrdinary is 67
Edit
I need the variable name because I have this snippet of code which runs before each TestSuite in Katalon Studio, basically it gives you the flexibility of passing GlobalVariables using a katalon.features file. The idea is from: kazurayam/KatalonPropertiesDemo
#BeforeTestSuite
def sampleBeforeTestSuite(TestSuiteContext testSuiteContext) {
KatalonProperties props = new KatalonProperties()
// get appropriate value for GlobalVariable.hostname loaded from katalon.properties files
WebUI.comment(">>> GlobalVariable.G_Url default value: \'${GlobalVariable.G_Url}\'");
//gets the internal value of GlobalVariable.G_Url, if it's empty then use the one from katalon.features file
String preferedHostname = props.getProperty('GlobalVariable.G_Url')
if (preferedHostname != null) {
GlobalVariable.G_Url = preferedHostname;
WebUI.comment(">>> GlobalVariable.G_Url new value: \'${preferedHostname}\'");
} else {
WebUI.comment(">>> GlobalVariable.G_Url stays unchanged");
}
//doing the same for other variables is a lot of duplicate code
}
Now this only handles 1 variable value, if I do this for say 20 variables, that is a lot of duplicate code, so I wanted to create a helper function:
def setProperty(KatalonProperties props, GlobalVariable var){
WebUI.comment(">>> " + var.getName()" + default value: \'${var}\'");
//gets the internal value of var, if it's null then use the one from katalon.features file
GlobalVariable preferedVar = props.getProperty(var.getName())
if (preferedVar != null) {
var = preferedVar;
WebUI.comment(">>> " + var.getName() + " new value: \'${preferedVar}\'");
} else {
WebUI.comment(">>> " + var.getName() + " stays unchanged");
}
}
Here I just put var.getName() to explain what I am looking for, that is just a method I assume.
Yes, this is possible with ASTTransformations or with Macros (Groovy 2.5+).
I currently don't have a proper dev environment, but here are some pointers:
Not that both options are not trivial, are not what I would recommend a Groovy novice and you'll have to do some research. If I remember correctly either option requires a separate build/project from your calling code to work reliable. Also either of them might give you obscure and hard to debug compile time errors, for example when your code expects a variable as parameter but a literal or a method call is passed. So: there be dragons. That being said: I have worked a lot with these things and they can be really fun ;)
Groovy Documentation for Macros
If you are on Groovy 2.5+ you can use Macros. For your use-case take a look at the #Macro methods section. Your Method will have two parameters: MacroContext macroContext, MethodCallExpression callExpression the latter being the interesting one. The MethodCallExpression has the getArguments()-Methods, which allows you to access the Abstract Syntax Tree Nodes that where passed to the method as parameter. In your case that should be a VariableExpression which has the getName() method to give you the name that you're looking for.
Developing AST transformations
This is the more complicated version. You'll still get to the same VariableExpression as with the Macro-Method, but it'll be tedious to get there as you'll have to identify the correct MethodCallExpression yourself. You start from a ClassNode and work your way to the VariableExpression yourself. I would recommend to use a local transformation and create an Annotation. But identifying the correct MethodCallExpression is not trivial.
no. it's not possible.
however think about using map as a parameter and passing name and value of the property:
def printSomething(Map m){
println m
}
printSomething(ordinary:58)
printSomething(extraOrdinary:67)
printSomething(ordinary:11,extraOrdinary:22)
this will output
[ordinary:58]
[extraOrdinary:67]
[ordinary:11, extraOrdinary:22]
I could definitely use some help here. I have an excel sheet from where I am getting values and doing some validations on them, I am checking if both values match. My code is as follows:
#Unroll ("For #calcToCheck.tr_date_class")
def "I check flag value #calcToCheck.tr_date "(CalculationClass calcToCheck) {
expect:
flag==calcToCheck.result
where:
calcToCheck << calInputParameters()
}
def calInputParameters() {
//some logic to get values from SQL and getting flag
return calcsToCheck
}
This runs fine for one row but when I insert multiple rows in excel sheet, I just get output as one result. I would like to see each row's result. I thought adding #Unroll would take care of it showing me what rows it is displaying the results but it does not.
Here is some sample code you can run and then modify. Because you did not explain where flag comes from, I was just making up something, assuming it is also an unrolled parameter checked against whatever your helper method delivers.
package de.scrum_master.stackoverflow
import spock.lang.Specification
import spock.lang.Unroll
/**
* See https://stackoverflow.com/q/48410722/1082681
*/
class PseudoExcelTableTest extends Specification {
#Unroll//("For #calcToCheck.tr_date_class")
def "I check flag value #calcToCheck.tr_date"(CalculationClass calcToCheck, int flag) {
expect:
flag == calcToCheck.result
where:
calcToCheck << calInputParameters()
flag << [11, 22, 33]
}
def calInputParameters() {
def calcsToCheck = new ArrayList<CalculationClass>()
calcsToCheck.addAll(
new CalculationClass(result: 11, tr_date: "eleven", tr_date_class: "short"),
new CalculationClass(result: 22, tr_date: "twenty-two", tr_date_class: "long"),
new CalculationClass(result: 33, tr_date: "thirty-three", tr_date_class: "normal")
)
return calcsToCheck
}
static class CalculationClass {
int result
String tr_date
String tr_date_class
}
}
As for the unrolled method names, you mix up two things:
Naming the methods via #Unroll parameter, which in my example would result in method names For short, For long, For normal. This has preference over the second option:
Naming the methods via normal method name, which in my example would result in method names I check flag value eleven, I check flag value twenty-two, I check flag value thirty-three.
I chose option 2 by commenting out the unroll parameter.
Expanding on this blog post, I am trying to use a category to create a simple DSL for use with the javax.measure (JSR-275) classes (similar to TimeCategory for time intervals)
However, I do not want to add boilerplate code for each of the possible available methods (getMeter, getMilliMeter, getKelvin, getSecond etc.). I thought overriding the getProperty(String) method would work, but alas, it looks like the getProperty method defined in the category is not used when accessing the property directly.
Here is some simplified code to demonstrate:
import javax.measure.quantity.Length;
import javax.measure.unit.Unit;
import javax.measure.Measure;
#Category(Number)
class LengthCategory {
public Measure<BigDecimal, Length> getProperty(String unit){
return Measure.valueOf(this,Unit.valueOf(unit));
}
}
use(LengthCategory){
println 3.getProperty("m") // this works
println 3.m // this reports a non-exisiting property
prinlln 3.'m' // as does this
}
Assuming other methods of dynamically adding properties to a runtime object (e.g. Expando, subclassing GroovyInterceptible, mixins and other metaclass manipulations) is not viable and I would really rather not have to manually code getters for every possible unit and SI prefix combination. There are obviously other ways to go about creating a DSL for measurements, but I would still like to understand why this method would not work.
Could someone explain why the getProperty method of the category does not override .propertyName usage? I am obviously missing something important about the resolution of property names using the metaclass during runtime.
I don't know why getProperty doesn't work on categories. But you can define a get method on them that does basically the same (i think). This works:
#Category(Number)
class LengthCategory {
def get(String unit) {
"$this $unit"
}
}
use (LengthCategory) {
println 3.m // 3 m
println 3.'m' // 3 m
}
As far as I can tell, you can't actually extend Integers with full (i.e., readable and writable) properties using Category -- only with methods.
You can extend an Integer using read-only properties by using the method version of the property. You can even make it writable by including a set method. However, there doesn't seem to be a way to store the value passed in other than in a static variable and that ends up affecting all Integers.
Example:
$ cat catprop
#!/usr/local/bin/groovy
#Category(Integer)
class CatInteger {
private static String str = "default"
public static String setN(Integer i, String _str) { str = _str }
public static String getN(Integer i) { return str }
}
use (CatInteger) {
3.n = "333a"
println "3.n is " + 3.n
3.n = "333b"
println "3.n is " + 3.n
4.n = "444"
println "4.n is " + 4.n
println "3.n is " + 3.n
}
$ catprop
3.n is 333a
3.n is 333b
4.n is 444
3.n is 444
$
Note that in the last line 3.n return "444" because the stored field is static. I suppose that one could use a private HashMap and store a value for every Integer accessed, but that's too ugly to contemplate.
Another possibility would be to use the MetaClass Interface's getProperty() and setProperty(). However, I haven't looked into that so I don't know if it would work or not (just a thought).
Nice answer, but not sure, if you's still want to use JSR-275 now that JSR-363 is final?;-)