Groovy Spock: is it possible to make "shared" a group of variables? - groovy

I have a big group of variables in my spock test. First half of them are objects which need some serious effort to be created, and second one are the constants. I want to mark all these objects with #Shared annotation. But actually may be there is a better and shorter way to evaluate this task?
Placing 20 or 30 similair annotations one after one is not a groovy way, I suppose..

You could have a single context, so one #Shared
class Test extends Specification {
#Shared context = [
one: new One(),
two: new Two()
]
def "Use of context"() {
expect:
context.one.//
context.two.//
}
}

Related

Accessing a variable defined in another function in Groovy

I am new to Groovy.
I have a function in which I am writing a value to map.
def addTraceEntry(key, value) {
def traceability = [:]
traceability[key] = value.trim()
println "This print happens in function addTraceEntry " + traceability
}
I have another function that needs to verify whether the above function works properly.
def testAddTraceEntry() {
def key = 'test_key'
def value = 'test_value'
addTraceEntry(key, value)
println "This print happens in function testAddTraceEntry " + traceability
assert value == traceability[key]
}
I am invoking the testAddTraceEntry() function using the function name:
testAddTraceEntry()
When I run this, I get the ERROR:
This print happens in function addTraceEntry [test_key:test_value]
Caught: groovy.lang.MissingPropertyException: No such property: traceability for class: HelloWorld
groovy.lang.MissingPropertyException: No such property: traceability for class: HelloWorld
at HelloWorld.testAddTraceEntry(HelloWorld.groovy:53)
at HelloWorld.run(HelloWorld.groovy:57)
In the function testAddTraceEntry it clearly does not know the value of traceability so seems like its giving an ERROR for that.
I tried to return the value of traceability.
def addTraceEntry(key, value) {
def traceability = [:]
traceability[key] = value.trim()
println "This print happens in function addTraceEntry " + traceability
return traceability
}
But this yields the same ERROR.
There are a bunch of things worth mentioning after seeing the code you have wrote.
First thing - the scope of variables and encapsulation. Let's throw away technicalities for a moment and focus on something even more important. In method addTraceEntry you persist some state, which is fine. However, the implementation of the method testAddTraceEntry reveals that this method tries to know way to much about the implementation details of addTraceEntry. It encapsulates (hides in other words) persistence logic (from the API point of view you, as a caller, don't know that it persists key and a value inside the map) and that is why testAddTraceEntry should never ever make assumptions that calling this method mutated some structure. If you do so, then:
your test method contracts side effects and not the expected business logic (storing data in some kind of global map - don't do it. Ever)
your test blocks any evolution of tested method implementation - imagine, that you decided to store key and value in a different structure. You may do it without breaking any API contract (your function produces the same results), but the test method will fail and you will have to modify it.
Second thing - your addTraceEntry method always produces a map with a single entry. It doesn't make much sense and if you call your function let's say 4 times you will end up with 4 maps where each one of them contain a single key mapped to a single value.
There are at least various ways to improve implementation of your methods. The simplest thing you can do is to implement a class that encapsulates logic for storing keys and values. Consider following example:
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
class TraceEntriesStorage {
private final ConcurrentMap<String, Object> entries = [:] as ConcurrentHashMap
def addTraceEntry(String key, Object value) {
entries.put(key, value)
}
def containsTraceEntry(String key) {
return entries.containsKey(key)
}
def retrieveTraceEntryForKey(String key) {
return entries.get(key)
}
}
This is a simple class with 3 short methods. It stores trace entries inside the internal concurrent map (to solve problems with concurrent access). Now, your test method could look like this:
def storage = new TraceEntriesStorage()
storage.addTraceEntry("test_key", "test_value")
assert storage.containsTraceEntry("test_key")
assert storage.retrieveTraceEntryForKey("test_key") == "test_value"
You create an instance of this class, you add an entry and you check if methods containsTraceEntry and retrieveTraceEntryForKey return expected values. As you can see it doesn't matter where we stored this trace entry - it matters that the class we have implemented behaves as expected. To make this test method even better you could add an assertion that checks if there is no trace entry for test_key before we actually insert it - this way we know that adding trace entry change internal state of the class. But what is nice in this approach is that as long as we don't break the contract, we can experiment and modify implementation of TraceEntriesStorage. Because what is most important - adding trace entries have to allow to retrieve them back from the object. How it gets stored, where it gets stored - it doesn't matter.
I hope you find this answer useful and it will help you in learning Groovy and designing a better programs. Happy hacking!
You need to combine adding the return statement to addTraceEntry() with assigning the returned value to a variable in testAddTraceEntry():
def traceability = addTraceEntry(key, value)

Puppet: how can i pass value from inherited class to base class?

How can I pass value from inherited class to base class using puppet?
You can see below a simplified code for my trials.
class executor::app($base_dir = "/usr/local",
$run_command = undef,
$prefix_naming = undef) {
}
class app1(
$base_dir = ::app1::params::base_dir,
$prefix_naming = "reader",
$run_command = " ") inherits executor::app{
}
OK, for starters lets assume you have these classes in module format. If not, then that should be the first order of business.
Second, avoid inheritance. There is almost always a better way to do it. Especially don't inherit across modules. About the only time I can think it's useful is for defaulting class parameters.
The base_dir on class app1 will not get the default unless the class inherits cea::params::base_dir (leading :: not needed). Again, across modules shouldn't be done. app1::params much better -- or just put in a sane default and eliminate the need to inherit parameters all together.
For your actual question, if you want to get a variable in another class you can just reference it. Keep in mind that puppet doesn't guarantee compile order so you should tell it to evaluate the other class first:
class executor::app {
Class['app1'] -> Class['executor::app']
$other_app_var = $app1::base_dir
}
Or throw this data in hiera and look up the value.

Groovy closure not work with static final field from super class

class Parent {
final static String newLine = "*"
}
class Child extends Parent{
List body = [3, 4, 5]
String toString() {
def str = new StringBuilder()
body.each { str.append(it + newLine) }
str
}
}
def c = new Child()
println c
The above is one trivial sample to illustrate the problem. It couldn't be compiled using Groovy plugin on Eclipse. Remove either final or static in the field of super class solves the problem. However, I have no idea why it's the case.
http://groovy.codehaus.org/Groovy+Beans
In this link it mentions the rules for property and fields used in Groovy. I suppose the one applied should be the last one, i.e. meta class. Unfortunately, I still couldn't understand the behavior.
The behavior is reproduced consistently in all versions of Groovy. Maybe someone could report one bug to Groovy team. I have never done this before. It would be more efficient if someone experienced could do that.
This is most probably https://issues.apache.org/jira/browse/GROOVY-5776 which is more difficult to fix than it looks like
As blackdrag already pointed out: it's a bug.
But another workaround is to add the protected keyword:
protected final static String newLine = "*"

This code looks like, groovy will result in bad performance? Is it so?

I have been reading Groovy for a month or so. Recently i have came across the following code:
class MyBean implements Serializable {
def untyped
String typed
def item1, item2
def assigned = 'default value'
}
And when I do this :
def bean = new MyBean()
assert 'default value' == bean.getAssigned()
However the above code makes GroovyBeans very very impressive, but still my question is this:
Even though we haven't created the getter function(getAssigned()), groovy does for us. So is that groovy produce this for all class's even though we are not intended to work in GRoovyBeans? This means that for all class's it creates the setter and getter, even though we wont want? Is this is not the performance issue? Or else my view is worng?
Adding a method to a class won't cause a performance issue, as it doesn't have to be called.
If you want direct access to the property, you can use the Java field operator:
bean.#assigned

Can I redefine String#length?

I'd like to re-implement a method of a Java class. For example, for "hi".length() to return 4. (How) Can I do that?
I know using SomeClass.metaClass I can get a reference to an existing method and define new (or overriding) method, but I can't seem to be able to do that for existing Java methods.
Using Groovy, you can replace any method (even those of final classes) with your own implementation. Method replacement in Groovy uses the meta-object protocol, not inheritance.
Here's the example you requested, i.e. how to make String.length() always return 4
// Redefine the method
String.metaClass.invokeMethod = { name, args ->
def metaMethod = delegate.metaClass.getMetaMethod(name, args)
def result = metaMethod.invoke(delegate, args)
name == 'length' ? 4 : result
}
// Test it
assert "i_do_not_have_4_chars".length() == 4
Seems like it could be possible by abusing String metaClass. But the attempt I've done so far in groovy console didn't led to the expected result :
def oldLength = String.metaClass.length
String.metaClass.length = { ->
return oldLength+10;
}
println "hi".length()
outputs the sad 2
I think you could take a look at Proxy MetaClass or Delegating metaClass.
If you did redefine it, it would only work in Groovy code. Groovy can't change the way Java code executes.
In Groovy, "hi".length() is roughly equivalent to this Java:
stringMetaClass.invokeMethod("hi","length");
Because Groovy doesn't actually call length directly, metaClass tricks work in Groovy code. But Java doesn't know about MetaClasses, so there is no way to make this work.
Although this question is very old I like to point out another way (at least for newer Groovy versions) .
The length() method in java.lang.String is implemented from java.lang.CharSequence interface. In order to reimplement the method using the String-metaClass you need to "override" the method in the metaClass of the interface first.
CharSequence.metaClass.length = { -> -1}
String.metaClass.length = { -> 4 }
assert "i_do_not_have_4_chars".length() == 4
The solution using String.metaClass.invokeMethod changes the behaviour of all String-methods and is problematic. For instance, simply invoking "asdf".size() leads to an exception on my setup.

Resources