Does positive assert message exist in Groovy? - groovy

There is a Negative Groovy Assert with Message like:
def name = "John"
assert name == "Peter" : "Name should be John"
Which gives output:
Caught: java.lang.AssertionError: Name should be John. Expression: (name == Peter). Values: name = John
java.lang.AssertionError: Name should be John. Expression: (name == Peter). Values: name = John
But if the statement is true there is no information in the log. So when you (or your coworker) checks the log later (coworker does not know the checks you implemented) you have no idea about assertions which had place.
So I'd like to log positive assertions. Something like (adding positive message ? "Name is " + name):
def name = "Peter"
assert name == "Peter" : "Name should be John" ? "Name is " + name
Does it exists? I know I can log message after the assertion e.g.: log("Assert is correct: Name is " + name) but I'd like to do it in one line assertion.

There is no such thing like positive assertion message, because it does not make much sense in general. Let's consider example you have posted:
assert name == "Peter" : "Name should be John" ? "Name is " + name
When expression name == "Peter" evaluates to false it is straightforward what happens - java.lang.AssertionError is thrown with message Name should be John. Positive branch in this case is not straightforward - where does the message Name is ${name} should be log to? Using simple println or maybe using slf4j's log.info() or log.debug? Where is explicit decision about that? And what if I don't want to have such information in my logs, because it only produces overhead?
Of course you could define your own method like:
void doAssert(boolean expr1, String errorMessage, Closure<Void> onSuccess = null) {
assert expr : errorMessage
onSuccess?.call()
}
and then do:
doAssert name == "Peter", "Name should be John", {
log.info("Name is ${name}")
}
but in long term this might be an overkill - you create a closure object only for some debugging information. You can always modify this method and comment out onSuccess?.call() to limit some overhead created while calling a closure, although closure's object (and related Java anonymous class) gets created anyway.
But I guess you see that above method requires exactly same amount of effort as:
assert name == "Peter" : "Name should be John"
log.info("Name is ${name}")
I would rather suggest to use assertions as they were designed and don't produce much overhead. You can always log important information, but it has to be something that makes sense for maintainers. I don't see much value in getting informed that specific assertion was satisfied - I know that because program continued computations. Hope it helps you making right decision.

Related

Setting member variables by name in Spock

I am writing a Spock integration test for a function that takes a DTO, that has a lot of member variables. The functionality to test is that one user role is allowed to change some member variables, but if that user tries to change one of the other fields, it should fail.
I don't want to manually write a test for every single field, so I figured, I could be using a where: table like so:
where:
fieldname | value | isAccepted
"fieldA" | "val" | true
"fieldB" | "val" | false
...
For that, I'd have to be able to set the fields by the name in a string. In Java I'd have to use reflection for that. In Python I could just use setattr(object, fieldname, value) for that.
Since I writing the test in Groovy, is there something idiomatic for that?
Yes there is an idiomatic way to dynamically access fields an methods in groovy. You just use a GString to reference the field.
class A {
String foo = 200
String bar = 400
}
def a = new A()
def field = "foo"
println a."$field"
field = "bar"
println a."$field"
a."$field" = 600
println a.bar
Outputs
200
400
600
Test it in the Groovy Web Console

Is it possible to get the name of variable in Groovy?

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]

'No signature of method' when attemptng to set property value in project using Groovy

Encountering the following issue when attempting to set a property at project level.
No signature of method: com.eviware.soapui.impl.wsdl.WsdlProject.setPropertyValue() is applicable for argument types: (java.lang.String, java.math.BigDecimal) values: [TitleId, 1] Possible solutions: setPropertyValue(java.lang.String, java.lang.String), getPropertyValue(java.lang.String)
The code works when i set the project property the first time with 'rs' however the second time it refuses to store it and returns the error message above.
The conditional query is as follow:
if (rs !=null) {
log.info ("Result " + (rs[0]))
project.setPropertyValue("TitleId", rs[0]); //this works
}
else {
def cdsTitle = "SELECT TITLE_TYPE, TITLE_TEXT FROM PARTY WHERE PARTY_KEY ='" + context.expand('${#Project#PartyKey}') + "'";
def titleType= sqlCDS.firstRow(cdsTitle)
project.setPropertyValue("TitleId", titleType[0]); //This is the statement which causes the error message
}
'titleType' returns two values and I'm trying to set it to be the first hence the '[0]'. I have tried to store this value in its own variable and use that to set the property however this still faulted with the same error message.
i.e.
def cdsTitle = "SELECT TITLE_TYPE, TITLE_TEXT FROM PARTY WHERE PARTY_KEY ='" + context.expand('${#Project#PartyKey}') + "'";
def titleType= sqlCDS.firstRow(cdsTitle)
def ttype = titleType[0]; //returns 1
project.setPropertyValue("TitleId", ttype;)
}
Any help would be much appreciated. Thanks
It seens that rs[0] (as the 2nd argument) has a proper type and (as you wrote) it was accepted.
Then the program goes along the else path and uses titleType[0]
as the 2nd argument.
Are you sure that it has the same type?
Look thoroughly at the error message:
No signature of method: ... is applicable for argument types:
(java.lang.String, java.math.BigDecimal) values: [TitleId, 1]
The above message says that:
the 1st argument was TitleId (I assume a string and it was accepted),
the 2nd argument is 1 (a BigDecimal of value 1) and probably there
is something wrong about the type.
Maybe you should cast it to another type?
titleType[0].toString() seems to fix the problem

How to get class field identifier as String in Groovy?

Suppose simple Groovy class (POGO):
class Person { String name; int age }
How to get chosen identifier at runtime? In other words identifier name (like "name" and "age") used in source code by programmer - not value held in variable of this identifier (like "James" and "32").
With result solution (if there is any) it would be possible to write something like this (syntactically wrong pseudo code):
assert new Person().name.identifierAsString == 'name'
assert new Person().age.identifierAsString == 'age'
Please note I'm not trying to get list of field names (like Person.declaredFields), but compile-time safe way to get field identifier as String.
I wrote a lib sometime ago to write type-checked queries. It might be what you are after:
import org.latitude.Latitude
Customer customer = Latitude.forClass Customer
assert customer instanceof Customer
assert customer.name == "name"
I'm not certain what you're trying to accomplish, but you can get all of the declared field names like so:
assert ['name', 'age'] == Person.declaredFields.findAll { !it.synthetic }.collect { it.name }
And otherwise declaredFields is a list of java.lang.reflect.Field that you can use.

Groovy DSL getting anonymous String assignments

I have a DSL that looks like this:
aMethod {
"a name"
"another name"
"and a third name"
}
My Problem is that I'm unable to access the three string, because calling the closure only returns the last statement. I tried to override the constructor of String(char[] value) which is called when an anonymous String-statement occurs:
def original
// primitive way to get the String(char[])-constructor
String.class.constructors.each {
if(it.toString() == "public java.lang.String(char[])") {
original = it
}
}
// overriding the constructor
String.metaClass.constructor = { char[] value ->
def instance = original.newInstance(value)
// ... do some further stuff with the instance ...
println "Created ${instance}"
instance
}
// teststring to call String(char[] value)
"teststring"
Unfortunately it didn't work and I thought anyway that it is quite complicated.
Thank you for the comments. Actually it would be great to define everything without quotes. But: After having a dsl that can be translated to java objects I'd loved to have additional annotations in my language at development time. I want to annotate duplicate names and so on. The IDE's I know better, Intellij and Eclipse handle Strings "a name" as one PSI-Elements. Splitting these elements can be very inconvinient ... I guess. I think statements in a closure like aMethod {a name} would result in an interpretation like aMethod {a.name}. That would mean that instead of having a StringLiteral Psi "a name", I would have an Object-Psi and a MethodCall-Psi or something like that. I don't know, and my next goal is just "parsing/creating" my java objects. Are you sure that it is impossible to override the String-Constructor?
Is any constructor called when you have a groovy script with this content:
"hello World"

Resources