How to avoid occurrence MissingPropertyException at absence of parameters from a template in Map and to replace not found values by null?
import groovy.text.StreamingTemplateEngine
import groovy.text.Template
class Test {
private static Writable binding(Map map, String string) {
Template template = new StreamingTemplateEngine().createTemplate(string)
return template.make(map)
}
static void main(String... args) {
def template = "\${test1} \${test2}"
def map = ["test1": "test1"]
print binding(map, template)
}
}
There is no configuration option to suppress this exception, however you can extend a map you pass to the template and change its behavior a bit. Consider following example:
import groovy.text.StreamingTemplateEngine
import groovy.text.Template
def string = '''
Dear <% out.print firstname %> ${lastname},
We <% if (accepted) out.print 'are pleased' else out.print 'regret' %>
to inform you that your paper entitled
'$title' was ${ accepted ? 'accepted' : 'rejected' }.
The conference committee.
'''
def map = [
firstname: 'test',
lastname: 'test',
accepted: true
]
Template template = new StreamingTemplateEngine().createTemplate(string)
println template.make(map)
It fails with the following exception:
Caught: groovy.text.TemplateExecutionException: Template execution error at line 4:
3: We <% if (accepted) out.print 'are pleased' else out.print 'regret' %> to inform you that your paper entitled
--> 4: '$title' was ${ accepted ? 'accepted' : 'rejected' }.
5:
groovy.text.TemplateExecutionException: Template execution error at line 4:
3: We <% if (accepted) out.print 'are pleased' else out.print 'regret' %> to inform you that your paper entitled
--> 4: '$title' was ${ accepted ? 'accepted' : 'rejected' }.
5:
at test.run(test.groovy:21)
Caused by: groovy.lang.MissingPropertyException: No such property: title for class: groovy.tmp.templates.StreamingTemplateScript1
... 1 more
It fails because we have defined 3 from 4 template variables (variable title is missing).
Solution: create wrapper for a Map
Let's fix it. We will do it by overriding map method containsKey(Object key) in a way that it always returns true (this method is used by the template engine and if it returns false, template engine throws an exception). We will create a wrapper class that encapsulates a map and delegates invocation of non existing methods to this wrapped class. We will call this class Bindings.
import groovy.text.StreamingTemplateEngine
import groovy.text.Template
class Bindings {
#Delegate private final Map map
Bindings(Map map) {
this.map = map
}
boolean containsKey(Object key) {
return true
}
}
def string = '''
Dear <% out.print firstname %> ${lastname},
We <% if (accepted) out.print 'are pleased' else out.print 'regret' %>
to inform you that your paper entitled
'$title' was ${ accepted ? 'accepted' : 'rejected' }.
The conference committee.
'''
def map = [
firstname: 'test',
lastname: 'test',
accepted: true
]
Template template = new StreamingTemplateEngine().createTemplate(string)
println template.make(new Bindings(map))
Output:
Dear test test,
We are pleased
to inform you that your paper entitled
'null' was accepted.
The conference committee.
There is no MissingPropertyException thrown anymore. However, as you can see, null is printed as null inside the String. If you would like to print an empty string instead, you can add Object get(Object key) method to Bindings and override its default behavior:
class Bindings {
#Delegate private final Map map
Bindings(Map map) {
this.map = map
}
boolean containsKey(Object key) {
return true
}
Object get(Object key) {
return map.getOrDefault(key, '')
}
}
If you do so, you will see the output similar to:
Dear test test,
We are pleased
to inform you that your paper entitled
'' was accepted.
The conference committee.
Hope it helps.
As an alternative, you can just use the groovy Map.withDefault method:
import groovy.text.StreamingTemplateEngine
import groovy.text.Template
def string = '''
Dear <% out.print firstname %> ${lastname},
We <% if (accepted) out.print 'are pleased' else out.print 'regret' %>
to inform you that your paper entitled
'$title' was ${ accepted ? 'accepted' : 'rejected' }.
The conference committee.
'''
def map = [
firstname: 'test',
lastname: 'test',
accepted: true
].withDefault { "<not found>" }
Template template = new StreamingTemplateEngine().createTemplate(string)
println template.make(map)
where the only change to the OPs code is the withDefault clause. Executing the above prints:
~> groovy test.groovy
Dear test test,
We are pleased
to inform you that your paper entitled
'<not found>' was accepted.
The conference committee.
As a side note, I wrote the streaming template engine as a contribution and a response to the limitations of the other template engines at the time a few years back. Glad to see it being used!
Though in hindsight it's far from perfect. Writing another one with a better internal approach has been on my list for aeons.
Related
Im losing it, I feel like im doing this right BUT cant figure out why this simple test fails
I got a feature file like so
Scenario: List all accounts in the tenant
Given that Keith has navigated to the tenant account list
When he views the accounts in the table that include name, name, name
Then he should also see 1,2,3 in the list
I got a definition file like so
this.When(/^(.*?) views the accounts in the table that include (.*)$/, (name: string, accountInformation: string) => {
return stage.theActorCalled(name).attemptsTo(
ViewAllAccountNames.inTheTableOf(listOf(accountInformation)),
);
});
I got a pageObject file like so
export class AccountTable {
// static displayingAll = Text.ofAll(AccountListUI.accountListView);
// static isDisplayingAllNames = Text.ofAll(Target.the('account names in the table').located(by.css('table tbody tr td:nth-child(2)')));
static AccountNames = Target.the('account names in the table').located(by.css('table tbody tr td:nth-child(2)'));
static AccountNumbers = Target.the('account numbers in the table').located(by.css('table tbody tr td:nth-child(1)'));
static isDisplayingAllNames = Text.ofAll(AccountTable.AccountNames);
static isDisplayingAllNumbers = Text.ofAll(AccountTable.AccountNumbers);
}
here is my class that does the work
constructor(private accName: string[]) {
}
static inTheTableOf(accName: string) {
return new ViewAllAccountNames(accName);
}
performAs(actor: PerformsTasks): PromiseLike<void> {
return actor.attemptsTo(
See.if(AccountTable.isDisplayingAllNames, items => expect(items).to.eventually.contain(this.accName))
);
}
}
when i debug through webstorm inside class ViewAllAccountNames i get
static inTheTableOf(accName: string) { accName: Array(3)
return new ViewAllAccountNames(accName); accName: Array(3)
}
then when i get to my See.if function I get
performAs(actor: PerformsTasks): PromiseLike<void> {
return actor.attemptsTo(
See.if(AccountTable.isDisplayingAllNames, items => expect(items).to.eventually.contain(this.accName)) AccountTable.isDisplayingAllNames: undefined
);
}
so my dilemma is this:
and I think it stems to my See.if function isnt setup in the correct way?
See.if(AccountTable.isDisplayingAllNames, items => expect(items).to.eventually.contain(this.accName))
Cucumber test run has failed.
1) Scenario: List all accounts in the tenant - e2e\features\get_account_list\get_all_accounts.feature:10
Step: When he views the accounts in the table that include name, name, name - e2e\features\get_account_list\get_all_accounts.feature:12
Step Definition: node_modules\serenity-js\src\serenity-cucumber\webdriver_synchroniser.ts:47
Message:
AssertionError: expected [ Array(5) ] to include [ 'name', 'name', 'name' ]
From: Task: <anonymous>
i am an idiot, I was using the wrong function, had to use the function
.to.eventually.contain.members
similar answer and description found here:
Answer
Went through the java docs of getAttribute. Couldn't understand the point mentioned as :
Finally, the following commonly mis-capitalized attribute/property
names are evaluated as expected: "class" "readonly"
Could someone confirm if webElement.getAttribute("class") shall return the class name of the element or not?
Edit : On trying this myself
System.out.println("element " + webElement.getAttribute("class"));
I am getting
org.openqa.selenium.NoSuchElementException
Note : The element does exist on the screen as I can perform actions successfully on the element :
webElement.click(); //runs successfully
Code:
WebElement webElement = <findElement using some locator strategy>;
System.out.println("element " + webElement.getAttribute("class"));
So the answer to the problem was answered on GitHub in the issues list of appium/java-client by #SergeyTikhomirov. Simple solution to this is accessing the className property as following :
webElement.getAttribute("className")); //instead of 'class' as mentioned in the doc
Method core implementation here : AndroidElement
According to this answer, yes you are doing it right. Your org.openqa.selenium.NoSuchElementException is thrown because selenium can't find the element itself.
The sidenote you have posted, about webElement.click() actually working, is unfortunately not included in the code you have posted. Since it is not a part of the actual question, I leave this answer without adressing it.
public String getStringAttribute(final String attr)
throws UiObjectNotFoundException, NoAttributeFoundException {
String res;
if (attr.equals("name")) {
res = getContentDesc();
if (res.equals("")) {
res = getText();
}
} else if (attr.equals("contentDescription")) {
res = getContentDesc();
} else if (attr.equals("text")) {
res = getText();
} else if (attr.equals("className")) {
res = getClassName();
} else if (attr.equals("resourceId")) {
res = getResourceId();
} else {
throw new NoAttributeFoundException(attr);
}
return res;
}
I'm using Geb and Spock to writing my Integration Test and here are my codes:
ITCase.groovy:
waitFor {
documents && documents[index]?.displayed && documents[index].checked?.displayed
}
documents[index].checked.click()
Page.groovy:
static content = {
documents {
moduleList DocumentListItemModule, $("#documents-list table tbody tr")
}
}
DocumentListItemModule.groovy:
static content = {
checked { $(".tst-doc-checkbox").find { it.displayed }}
}
Usually it worked well, but occasionally there will be error at line documents[index].checked.click():
Template 'content template 'checked' defined by XXX returned null for args: '[]'
How can I solve this problem?
I faced the same issue and solved it by waiting for the element. So in your case, I would try something like
static content = {
checked(wait: true) { $(".tst-doc-checkbox").find { it.displayed }}
}
The null safe operator in Groovy is great for reducing code and making things more readable. We can go from this:
def customer = getCustomer(custNo)
if(!customer)
throw new Exception("Invalid customer: ${custNo}")
def policy = customer.getPolicy(policyNo)
if(!policy)
throw new Exception("Invalid policy: ${policyNo}")
def claim = policy.getClaim(claimNo)
if(!claim)
throw new Exception("Invalid claim: ${claimNo}")
..to this...
def claim = getCustomer(custNo)?.getPolicy(policyNo)?.getClaim(claimNo)
But nothing's for free; using null/safe navigation, if claim is null, it's not immediately obvious what caused it: either custNo, policyNo, or claimNo may be invalid.
We could go back and start checking what's null, but that's counterproductive, and actually, it's not even possible since intermediate objects are not stored in variables.
So the question is: Is it possible to identify what was null when chaining method calls using null/safe navigation?
UPDATE
I took another stab at this using dynamic method invocation. It takes an init target (usually a dao) to initialize the object (customer in this case), and a map containing method names as strings (with arguments as values). Using an iterator, invokeChain simply traverses the map (chain); if anything in the chain returns null, identifying the method that caused it becomes trivial.
def invokeChain = { initTarget, chain ->
def obj
chain.eachWithIndex{ it, idx ->
//init obj from dao on first iteration only,
//remaining iterations get from obj itself
obj = (!idx) ? initTarget."$it.key"(it.value) : obj?."$it.key"(it.value)
if(!obj)
throw new Exception("${it.key}(${it.value}) returned null")
}
obj
}
Usage
Mock a customer dao for initTarget...I've inserted null as return type for getClaim(), which should throw an exception.
def static getCustomer = { custNo ->
[ getPolicy: { p ->
[getClaim:{ c ->
null //"Claim #${c}"
}]
}]
}
..using invokeChain, easy as pie:
def claim = invokeChain(this, [getCustomer:123, getPolicy:456, getClaim:789])
...throws exception, as expected:
Exception in thread "main" java.lang.Exception: getClaim(789) returned null
I like this approach because it's compact, readable, and easy to use; what do you think?
I think there is no clear way to do so.
I can be wrong, will check sources later, but safe navigation is a syntax sugar for if statement.
As a hack you can wrap your code with interceptor, trace last method call inside, and then use that info to proide error message.
It will not be cheap, and will cost you some code to realize interception and some performance while running. But you can achieve something like
mayFail("getCusomer", "getPolicy", "getClaim") {
getCustomer(custNo)?.getPolicy(policyNo)?.getClaim(claimNo)
} == "getPolicy" // failed on second step
EDIT: As #tim_yates proved, ?. is a syntax sugar with if construction behind. Thanks Vorg van Geir for the link, I have copied it here, to an answer. He say, it's outdated, and it looks like he is right. I have managed to make ProxyMetaClass work(in Groovy 2.0.6), so given way isn't totally broken. Now I need to specify exact classes to intercept, and I can not find a way to catch inherited method calls.(to simply intercept java.lang.Object)
def logInterceptor = new TracingInterceptor()
logInterceptor.writer = new StringWriter()
def intProxy = ProxyMetaClass.getInstance(Integer)
def stringProxy = ProxyMetaClass.getInstance(String)
intProxy.setInterceptor(logInterceptor)
stringProxy.setInterceptor(logInterceptor)
intProxy.use {
stringProxy.use {
println(("Hello" + "world").size().hashCode())
} }
println(logInterceptor.writer.toString())
All that hell may be wrapped in some utility code, but I highly doubt in necessarity of this. Performance overhead will be awful and some boilerplate code will remain.
The game is not worth the candle.
What about attributions and asserts?
def policy = customer?.policy
def claim = policy?.claim
def number = claim?.number
assert customer, "Invalid customer"
assert policy, 'Invalid policy'
assert claim, 'Invalid claim'
UPDATE:
You already found the solution, but i'd like to contribute with an interceptor idea:
The mock:
def dao = [
getCustomer : { custNo ->
[ getPolicy: { p ->
[getClaim:{ c ->
null //"Claim #${c}"
}]
}]
}
]
The interceptor:
class MethodCallInterceptor {
def delegate
def invokeMethod(String method, args) {
def result = delegate.invokeMethod(method, args)
if (result == null) {
throw new RuntimeException("$method returned null")
}
else {
new MethodCallInterceptor(delegate: result)
}
}
def getProperty(String property ) {
delegate[ property ]
}
void setProperty(String property, value) {
delegate[ property ] = value
}
}
The test:
def interceptedDao = new MethodCallInterceptor(delegate: dao)
try {
interceptedDao.getCustomer(123).getPolicy(456).getClaim(789)
assert false
} catch (e) {
assert e.message.contains( 'getClaim returned null' )
}
In my work, I have methods to return closures as inputs for markup builders. So, for testing purposes, can we make an expected closure and assert the expected one equal to the one returned by one method? I tried the following code, but the assert failed.
a = {
foo {
bar {
input( type : 'int', name : 'dum', 'hello world' )
}
}
}
b = {
foo {
bar {
input( type : 'int', name : 'dum', 'hello world' )
}
}
}
assert a == b
I do not think it will be feasible to assert the closures even after calling them.
//Since you have Markup elements in closure
//it would not even execute the below assertion.
//Would fail with error on foo()
assert a() != b()
Using ConfigSlurper will give the error about input() since the closure does not represent a config script (because it is a Markup)
One way you can assert the behavior is by asserting the payload (since you have mentioned MarkupBuilder). That can be easily done by using XmlUnit as below(mainly Diff).
#Grab('xmlunit:xmlunit:1.4')
import groovy.xml.MarkupBuilder
import org.custommonkey.xmlunit.*
//Stub out XML in test case
def expected = new StringWriter()
def mkp = new MarkupBuilder(expected)
mkp.foo {
bar {
input( type : 'int', name : 'dum', 'hello world' )
}
}
/**The below setup will not be required because the application will
* be returning an XML as below. Used here only to showcase the feature.
* <foo>
* <bar>
* <input type='float' name='dum'>Another hello world</input>
* </bar>
* </foo>
**/
def real = new StringWriter()
def mkp1 = new MarkupBuilder(real)
mkp1.foo {
bar {
input( type : 'float', name : 'dum', 'Another hello world' )
}
}
//Use XmlUnit API to compare xmls
def xmlDiff = new Diff(expected.toString(), real.toString())
assert !xmlDiff.identical()
assert !xmlDiff.similar()
Above looks like a functional test, but I would go with this test unless otherwise there is an appropriate unit test to assert two markup closures.