In Spock mocks impossible to verify mock against result: MissingPropertyException: No such property - groovy

I have some strange error when trying to check mock in then block with the result returned by the tested method, which I don't know beforehand. Here is a simple example:
import spock.lang.Specification
class MockingTest extends Specification {
private MyListener listener = Mock()
def 'test listener called'() {
when:
def service = new MyService(listener)
def message = service.foo()
then:
1 * listener.onEvent(message)
}
class MyService {
private MyListener listener;
MyService(MyListener listener) {
this.listener = listener
}
String foo() {
String message = "Hello " + new Random().nextInt(10);
listener.onEvent(message)
return message;
}
}
class MyListener {
void onEvent(String message) {
System.out.println(message);
}
}
}
And the error is:
No such property: message for class: MockingTest
groovy.lang.MissingPropertyException: No such property: message for class: MockingTest
at MockingTest.test listener called(MockingTest.groovy:14)
Event that
1 * listener.onEvent(message)
is placed in then block seems that Spock tries to initialize it early, even before when block is executed.
Is it any way to work around it, and check that mock is called with some local variable, not a constant?
The thing which is very simple to do with java + mockito appears to be very complex with Spock :(

You can use capture technique for this purpose, where you capture the first argument (it[0]) of the onEvent method call and assign it into pre-declared variable (capturedMessage):
def 'test listener called'() {
given:
def service = new MyService(listener)
String capturedMessage = null
when:
def message = service.foo()
then:
1 * listener.onEvent(_) >> { capturedMessage = it[0] as String }
message == capturedMessage
}
The problem in your example is that the interaction test (1 * listener.onEvent(message)) is executed before the foo() call is finished and then the message variable is not declared yet.
Side note: given is the right section for declaring and initialization of test data like the service.

Related

GroovyShell and Binding.setVariable() for callable objects

I am experimenting with some dynamic variable creation with GroovyShell and encountered an issue. First, the working code:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
defVar(glob)
shell.parse('test()').run()
This gives me the expected output:
--- hello ---
However, I want to call setVariable() dynamically when getVariable() is called, something like this:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable('test')) {
BindingTest.defVar(this)
}
return super.getVariable(name)
}
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
//defVar(glob)
shell.parse('test()').run()
But this fails with the below error:
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
at Script1.run(Script1.groovy:1)
at Script1$run.call(Unknown Source)
at BindingTest.run(BindingTest.groovy:23)
When I added tracing code like this:
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable(name)) {
BindingTest.defVar(this)
}
println("getVariable: ${name}: ${super.getVariable(name).getClass().getName()}")
return super.getVariable(name)
}
void setVariable (String name, def val) {
println("setVariable: ${name}: ${val.getClass().getName()}")
super.setVariable(name, val)
}
def getProperty(String name) {
println("getProperty: ${name}: ${super.getProperty(name)}")
return super.getProperty(name)
}
void setProperty (String name, def val) {
println("setProperty: ${name}: ${val.getClass().getName()}")
super.setProperty(name, val)
}
}
In the working case, I get the below output:
setVariable: test: Test
--- hello ---
In the non-working case, I get this output:
setVariable: test: Test
getVariable: test: Test
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
...
Two questions:
In the working scenario, why is there no getVariable?
In the non-working scenario, why is the Test object returned by getVariable getting rejected?
Note that this issue is specific to callable values. If I set a simple value such as a string, to test, then both approaches work fine. E.g., with this sort of a change:
...
static def defVar(def glob) {
glob.setVariable('test', '--- hello ---')
}
...
shell.parse('println(test)').run()
I get the below identical output with both approaches:
setVariable: test: java.lang.String
getVariable: test: java.lang.String
setVariable: test: java.lang.String
--- hello ---
Though, I am not sure why setVariable gets called twice. I couldn't find any documentation explaining these puzzling behaviors. Could anybody here shed some light on them?
Please note, all the code snippets have been simplified for the ease of demonstrating the problem rather than for their intended purpose
When you use a property as a callable fallback, the Binding.getVariable() method does not get involved. This behavior is controlled by the metaclass, and in your case, it all drives to the execution of the MetaClassImpl.invokePropertyOrMissing() method. This method determines if
test()
should invoke test.call() (in case of an existing property), or should it fallback to the missingMethod() method. Here is what this method implementation looks like:
private Object invokePropertyOrMissing(Object object, String methodName, Object[] originalArguments, boolean fromInsideClass, boolean isCallToSuper) {
// if no method was found, try to find a closure defined as a field of the class and run it
Object value = null;
final MetaProperty metaProperty = this.getMetaProperty(methodName, false);
if (metaProperty != null)
value = metaProperty.getProperty(object);
else {
if (object instanceof Map)
value = ((Map)object).get(methodName);
}
if (value instanceof Closure) { // This test ensures that value != this If you ever change this ensure that value != this
Closure closure = (Closure) value;
MetaClass delegateMetaClass = closure.getMetaClass();
return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);
}
if (object instanceof Script) {
Object bindingVar = ((Script) object).getBinding().getVariables().get(methodName);
if (bindingVar != null) {
MetaClass bindingVarMC = ((MetaClassRegistryImpl) registry).getMetaClass(bindingVar);
return bindingVarMC.invokeMethod(bindingVar, CLOSURE_CALL_METHOD, originalArguments);
}
}
return invokeMissingMethod(object, methodName, originalArguments, null, isCallToSuper);
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_5_X/src/main/groovy/groovy/lang/MetaClassImpl.java#L1262-L1287
Now, pay attention to the branch if (object instanceof Script) and how the binding variable gets retrieved. It tries to retrieve test variable from binding object using:
Object bindingVar = ((Script) object).getBinding().getVariables().get(methodName);
Your code would work if it was:
Object bindingVar = ((Script) object).getBinding().getVariable(methodName);
instead. But it's not.
You can make your second case working if you override getVariables() method instead of getVariable(String name), for instance:
class MyBinding extends Binding {
#Override
Map getVariables() {
return super.getVariables() + [
test: new Test()
]
}
}
Of course, your final implementation might be much more sophisticated. (E.g. you could get super.getVariables() map first, check which variables are missing and add a default variable only if the initial map was missing given variable.) But this is up to you.
Alternatively, consider using methodMissing instead of the binding variable fallback. It could make your code much easier to read and reason about.

UnsupportedOperationException when using Method Pointer Operator and MockFor

I had a test. It was using mockFor and was working and I was happy. Until something changed and I was forced to use a method pointer operator, and then my happiness was a just a memory.
Here a constricted example of the problem
import groovy.mock.interceptor.*
// I am testing againts a interface, not an actual class
// this is not negociable.
interface Person {
void sayHello(name);
}
// Setup (can do whatever I need)
def personMock = new MockFor(Person)
personMock.demand.sayHello { name -> println "Hello $name" }
def person = personMock.proxyInstance()
// End Setup
// Exercise (can not change)
def closureMethod = person.&sayHello.curry("Groovy!")
closureMethod()
// End Exercise
personMock.verify(person)
Which would be the safest and simplest way to fix the test?
Currently, the test fails with java.lang.UnsupportedOperationException
Even when I am testing against a Interface, nothing prevents me from creating a mock class that implements the interface and do a poor-men verify demands.
import groovy.mock.interceptor.*
// I am testing against a interface, not an actual class
// this is not negociable.
interface Person {
void sayHello(name);
}
class PersonMock implements Person {
def calls = []
void sayHello(name) { calls << "sayHello" }
}
// Setup (can do whatever I need)
def person = new PersonMock()
// End Setup
// Exercise (can not change)
def closureMethod = person.&sayHello.curry("Groovy!")
closureMethod()
// End Exercise
assert person.calls == ['sayHello']

Unexpected behaviour skips Agent.send method second time through when delegating methods to Agent protected value

I have been trying to do a small Groovy project, and wanted a ConcurrentLinkedHashSet, but Java doesn't provide one. So I set about creating my own using a Gpars agent to 'protect' an ordinary LinkedHashSet. I then created my wrapper class to hold the agent, and delegated the methods on my class to the internal Agent like this (this version is using methodMissing as delegation approach). I tried Groovy interceptable/invokeMethod and could get it to work either
class ConcurrentLinkedHashSet /*implements GroovyInterceptable*/ {
Agent $concurrentHashSet = new Agent (new LinkedHashSet())
/*Object[] toArray () {
$concurrentHashSet.val.asType(LinkedHashSet).toArray()
}*/
def asType (Set) {
$concurrentHashSet.val
}
//set up delegation action to pass all methods to the agent to execute
def /*invokeMethod*/methodMissing (String name, args){
def result
System.out.println "methodMissing with $name and $args on $this"
System.out.println "agent value is : ${$concurrentHashSet.val.dump()} is instance of: ${$concurrentHashSet.getClass()}"
$concurrentHashSet {
System.out.println "\t\tconcHashSet methodMissing: it is of type ${it.getClass()} so called $it.invokemethod ($name, $args) "
if (args == []) {
System.out.println "\t\tconcHashSet methodMissing: invoke method '$name' with no args "
result = it."$name"()//it.invokeMethod (name)
} else {
System.out.println "\t\tconcHashSet methodMissing: invoke method '$name' with args $args"
result = it.invokeMethod(name, *args)//"$name" (*args)//it.invokeMethod(name, args)
}
System.out.println "\tconcHashSet methodMissing: 'it' is now showing as > '$it'"
"success"
}
//System.out.println "agent protected value is now : " + $concurrentHashSet.val + " and result now $result"
System.out.println "agent protected value is now : " + $concurrentHashSet.val.dump() + " and result now $result"
$concurrentHashSet.val
}
}
however when I try to use this - it works first time through, my strings is added, but on the second call on the same missing method the agent.send call is never made, and gets skipped.
So my simple script consumer looks like this
// delegates add method via agent.send first time through but not after !
ConcurrentLinkedHashSet conhs = new ConcurrentLinkedHashSet ()
conhs.add ('col1')
println "1st conHashSet as list : ${conhs.toArray()}"
conhs.add ('col2')
println "2nd conHashSet as list : ${conhs.toArray()}"
// direct calls on an agent using invokeMethod
Agent myHash = new Agent (new LinkedHashSet())
myHash {it.invokeMethod ('add', 'col1')}
println "1st agentHashSet as list : ${myHash.val.toArray()}"
myHash {it.invokeMethod ('add','col2')}
println "2nd agentHashSet as list : ${myHash.val.toArray()}"
and my simple trace log looks like this on the console output
methodMissing with add and [col1] on org2.softwood.rules.utils.ConcurrentLinkedHashSet#3b0090a4
agent value is : <java.util.LinkedHashSet#0 map=[:]> is instance of: class groovyx.gpars.agent.Agent
concHashSet methodMissing: it is of type class java.util.LinkedHashSet so called [] (add, [col1])
concHashSet methodMissing: invoke method 'add' with args [col1]
concHashSet methodMissing: 'it' is now showing as > '[col1]'
agent protected value is now : <java.util.LinkedHashSet#2eaeb1 map=[col1:java.lang.Object#6a2f6f80]> and result now true
methodMissing with toArray and [] on org2.softwood.rules.utils.ConcurrentLinkedHashSet#3b0090a4
agent value is : <java.util.LinkedHashSet#2eaeb1 map=[col1:java.lang.Object#6a2f6f80]> is instance of: class groovyx.gpars.agent.Agent
agent protected value is now : <java.util.LinkedHashSet#2eaeb1 map=[col1:java.lang.Object#6a2f6f80]> and result now null
1st conHashSet as list : [col1]
methodMissing with add and [col2] on org2.softwood.rules.utils.ConcurrentLinkedHashSet#3b0090a4
agent value is : <java.util.LinkedHashSet#2eaeb1 map=[col1:java.lang.Object#6a2f6f80]> is instance of: class groovyx.gpars.agent.Agent
agent protected value is now : <java.util.LinkedHashSet#2eaeb1 map=[col1:java.lang.Object#6a2f6f80]> and result now null
methodMissing with toArray and [] on org2.softwood.rules.utils.ConcurrentLinkedHashSet#3b0090a4
agent value is : <java.util.LinkedHashSet#2eaeb1 map=[col1:java.lang.Object#6a2f6f80]> is instance of: class groovyx.gpars.agent.Agent
agent protected value is now : <java.util.LinkedHashSet#2eaeb1 map=[col1:java.lang.Object#6a2f6f80]> and result now null
2nd conHashSet as list : [col1]
1st agentHashSet as list : [col1]
2nd agentHashSet as list : [col1, col2]
As you can see on the first attempt at delegation you can see the 'concHashSet methodMissing:' trace and the agent calls invokeMethod on it in the agent to effect adding the element.
On the second call to conhs.add ('col2') the agent.sand call never happens and so my extra item never gets added.
This is annoying as I thought I had an easy way to create my ConcurrentLinkedHashSet, but the code doesn't work. What mechanism could I use to get the right outcome?
As you can see when I invokeMethod (add) directly on an Agent<LinkedHashSet> it works just fine. In my real consuming class if I replace my ConcurrentLinkedHashSet with an ordinary LinkedHashSet it works a dream, but isn't thread safe. I wanted create a thread safe version which depended on trying to get this to work.
I guess I could try and replace the Agent and just use synchronize blocks round my LinkedHashSet - but its a bit ugly - I thought the Gpars Agent would sort all this for me as a general solution pattern as a wrapper with delegation.
PS I tried another tack and this sort of works I think, but doesn't feel right - it uses #Synchronise on invokeMethod on class that implements GroovyInterceptable to achieve a thread safe call when delegating. I am not sure if this truly thread safe or not.
class ConcurrentLinkedHashSet2 implements GroovyInterceptable{
#Delegate private LinkedHashSet $mySet = new LinkedHashSet()
#Synchronized
def invokeMethod (String name, args) {
System.out.println "call to $name intercepted invoke using metaclass invoke"
ConcurrentLinkedHashSet2.metaClass.getMetaMethod(name).invoke (this, *args)
}
}
It works as expected after commenting out the trace output:
System.out.println "\t\tconcHashSet methodMissing: it is of type ${it.getClass()} so called $it.invokemethod ($name, $args) "
This line causes an unhandled exception to be thrown from the agent, which terminates the agent.
Vaclav spotted my error - my System.out.println had an error that caused an exception inside the body of the agent.
So I've built a corrected version of my original example:
/**
* using methodMissing to delegate calls
*
* Created by William on 29/12/2016.
*/
class ConcurrentLinkedHashSet2 implements GroovyInterceptable {
Agent $concurrentHashSet = new Agent (new LinkedHashSet())
//set up delegation action to pass all methods to the agent to execute
def invokeMethod (String name, args){
DataflowVariable invokeResult = new DataflowVariable()
$concurrentHashSet {
invokeResult << it?.invokeMethod(name, args)
}
invokeResult.val
}
}
This led to a more generalised answer in the Genie class - I hope this maybe of use to others. In this version decided to delegate equals, and toString to wrapped variable, as class was hanging, this fixes that. This version takes any Class or an instance variable and wraps it with thread safe Agent, and then delegates method calls to the wrapped variable - hence the 'Genie' moniker.
/**
* Thread safe container for instance of any type, where the
* genies invokeMethod is used to delegate calls to agent protected
* value. If the genie provides local methods, then the
* delegation calls the local method
*
* Created by William on 29/12/2016.
*/
class Genie<T> implements GroovyInterceptable {
Agent $concurrentVariable = new Agent ()
Genie (Class clazz) {
assert clazz instanceof Class
def instance = clazz.newInstance()
$concurrentVariable {
updateValue (instance)
}
def ref = $concurrentVariable.val
assert clazz.is (ref.getClass() )
}
Genie (instanceVariable) {
$concurrentVariable { updateValue (instanceVariable)}
}
void setVariable (instanceVariable) {
$concurrentVariable { updateValue (instanceVariable)}
}
def getVariable () {
$concurrentVariable.val
}
def getWrappedClass () {
$concurrentVariable.val?.getClass()
}
//set up delegation action to pass all methods to the agent to execute
def invokeMethod (String name, args) throws MissingMethodException {
DataflowVariable invokeResult = new DataflowVariable()
//if holding reference to a null then just try the call on the Genie instance
if ($concurrentVariable.val == null) {
invokeResult << this.metaClass.getMetaMethod("$name", args).invoke (this, args)
return invokeResult.val
}
//else look and see if method is expected to be called on the Genie instance
def listOfMethods = this.metaClass.getMethods().collect {it.name}
//we want delegation of toString() and equals(), and hashCode() to wrapped variable so remove from check of methods on Genie
listOfMethods.remove ("toString")
listOfMethods.remove ("equals")
listOfMethods.remove ("hashCode")
boolean methodMatched = listOfMethods.find {it == name}
if (methodMatched && this instanceof Genie) {
//see if there's a local method in Genie in scope, then call it
invokeResult << this.metaClass.getMetaMethod("$name", args).invoke (this, args)
} else {
def method = $concurrentVariable.val.metaClass.getMetaMethod(name, args)
if (method == null) {
invokeResult << $concurrentVariable.val.metaClass.invokeMissingMethod($concurrentVariable.val, name, args)
} else {
//delegate the method to the variable wrapped by the Agent<T>
$concurrentVariable {
MetaMethod validMethod = it.metaClass.getMetaMethod(name, args)
if (validMethod) {
invokeResult << validMethod.invoke(it, args)
} else invokeResult << null
}
}
}
invokeResult.val
}
}

How to access Closure properties from inside closure code?

I'm banging my head against this wall for more than a week now, let me explain briefly what I'm trying to achieve.
I have a DSL defined like this (brief example)
TestingSpec
.newInstance()
.sayHello() --> print "Hello "+something
.sayGoddbye() --> print "Goddbye"+something
Note the something in there that is not passed anywhere intentionally, because I want to limit the scope of use of my DSL by the will be users, so what I actually want is to somehow "inject" the something in that DSL instance programmatically. This is being done by using a Closure.
Myscript.execute( {
TestingSpec
.newInstance()
.sayHello() --> print "Hello "+something
.sayGoddbye() --> print "Goddbye"+something
})
and MyScript will invoke it by passing the something value to it, either by passing a parameter, or by adding a property or by adding a Binding (don't know what's the best, really)
close.call("Toni")
close.metaClass.something = "Toni"
def binding = new Binding()
binding.setVariable("something", "Toni")
close.setBinding(binding)
But now I'm stuck in what I thought it was going to be easy, how from the TestingSpec code can I access the Closure something? For instance
public newInstance() {
def instance = new TestingSpec()
instance.something = *MY CLOSURE SOMETHING*
return instance
}
I've tried several options, like messing with the this, owner and delegate of the closure, but couldn't do it.
Any hint will be very welcome...
Cheers.
I don't think that's doable. TestingSpec is too high on the stack to be aware he is being invoked from a closure. I'd like to suggest two solutions
1. Pass something to TestingSpec programmatically.
Myscript.execute( {
TestingSpec
.newInstance(something)
.sayHello() --> print "Hello "+something
.sayGoddbye() --> print "Goddbye"+something
})
2. Make the TestingSpec instance be created by MyScript
I like this solution more. It is about MyScript.execute({}) being responsible for creating and handling the lifecycle of TestingSpec:
class TestingSpec {
def something
def sayHello() {
println "Hello " + something
this
}
def sayGoddbye() {
println "Goddbye " + something
this
}
}
class Myscript {
static TestingSpec testingSpec
static execute(closure) {
def binding = new Binding(something: 'test for echo')
testingSpec = new TestingSpec(something: binding.something)
binding.testingSpec = testingSpec
closure.binding = binding
closure()
}
}
Myscript.execute( {
testingSpec // this is an instance, and not a static call
.sayHello() // print "Hello "+something
.sayGoddbye() // print "Goddbye"+something
})
Output:
$ groovy Spec.groovy
Hello test for echo
Goddbye test for echo
3. Delegate the closure to testingSpec
If you set the closure delegate to testingSpec, you can invoke its methods without referencing testingSpec directly, providing a very clean code. Note i also updated the example to remove MyScript:
class TestingSpec {
static TestingSpec testingSpec
static execute(closure) {
def binding = new Binding(something: 'test for echo')
testingSpec = new TestingSpec(something: binding.something)
binding.testingSpec = testingSpec
closure.delegate = testingSpec
closure.binding = binding
closure()
}
def something
def sayHello() {
println "Hello " + something
this
}
def sayGoddbye() {
println "Goddbye " + something
this
}
}
TestingSpec.execute( {
sayHello() // print "Hello "+something
sayGoddbye() // print "Goddbye"+something
})
4. Inject testingSpec as a parameter into your closure
As per your answer, a simple suggestion: consider giving a testingSpec var that you've built yourself to the user, since it allows more control and customization to you. Think a simple inversion of control (this allows testingSpec to see dynamic variables in the closure's binding):
.withDistinctNames({ testingSpec ->
testingSpec
.from(name)
.withAddress(email)
.sendEmail(template)
})
With a full test:
class TestingSpec {
def commands = []
static newInstance(listOfNames) {
new TestingSpec()
}
def sayHello() { commands << "sayHello"; this }
def waveGoddbye() { commands << "waveGoddbye"; this }
def withAddress(address) { commands << "withAddress $address"; this }
def sendEmail(template) { commands << "sendEmail $template"; this }
def withDistinctNames(closure) {
commands << "withDistinctNames"
closure.binding = new Binding(address: "sunset boulevard", template: 'email is $email')
closure(this)
this
}
}
test = TestingSpec
.newInstance([:])
.sayHello()
.withDistinctNames({ me ->
me
.withAddress(address)
.sendEmail(template)
})
.waveGoddbye()
assert test.commands == [
'sayHello',
'withDistinctNames',
'withAddress sunset boulevard',
'sendEmail email is $email',
'waveGoddbye'
]
I finally reach a solution to my problem that while not ideal it's "elegant" enough and "easy" to understand by would-be users of my DSL. So, what I want to do is allow the DSL users to write things like this:
TestingSpec
.newInstance()
.from(listOfNames)
.sayHello()
.withDistinctNames({
TestingSpec
.newInstance()
.from(*****) <---- problem
.withAddress()
.sendEmail(template)
})
.waveGoddbye()
There could be a number of with methods that basically serve as a filter+loop, so in this example it will filter the names to be unique and then apply the Closure to each name in turn. Of course this would be extremely easy to do by just doing
.withDistinctNames({
TestingSpec
.newInstance()
.from(it)
.withAddress()
.sendEmail(template)
})
or for more complicated cases
.withDistinctNames({ name, email, template ->
TestingSpec
.newInstance()
.from(name)
.withAddress(email)
.sendEmail(template)
})
The prob is I don't know who is going to use this DSL, it may not be people familiar with closures, parameters, functions, whatnots, it may be people even from outside my organization, so my goal was to keep it simple by passing the needed variables from the outer Spec to the inner Spec. In my TestingSpec implementation I would have:
public TestingSpec withAll(Closure k, Closure f) { // withAll is used by all with* methods
def result = k.call(root) // root is the entire listOfNames
def results = result
.collect {
// HERE'S THE PROBLEM, HOW TO INJECT ALL THE PARAMETERS INTO THE INNER SPEC?
f.call(it) // this line passes vars to the Closure, not the inner Spec, and I found no way for the Closure itself to inject them in the Spec
}
return this;
}
After I saw it was impossible to inject params in the inner Spec itself (that is yet to be instantiated) I tried to pass them to the Closure and from there to the Spec, like this:
.withDistinctNames({
TestingSpec
.newInstance()
.from(this) <-------------------
.withAddress()
.sendEmail(template)
})
I supposed that this would be the outer Spec that contains all the info I need, but it was not. I tried with this, this.thisObject, this.owner, this.delegate.
But then, with the help of the 3rd above suggestion I ended up doing this:
public TestingSpec withAll(Closure k, Closure f) {
f = f.rehydrate(f.getDelegate(), f.getOwner(), this)
def result = k.call(root)
def results = result
.collect {
this.parameters = [whatever I need]
f.call()
}
return this;
}
This way the this in the DSL is actually the outer Spec, so the users can now use the more intuitive "this" keyword. To avoid confusion I got rid of the .from() method, like this:
TestingSpec
.newInstance(listOfNames)
.sayHello()
.withDistinctNames({
TestingSpec
.newInstance(this) // my newInstance implementation can parse all the parameters as it wishes
.withAddress()
.sendEmail(template)
})
.waveGoddbye()
While it's not ideal is close enough, from the perspective of the potential users, I think. If somebody has suggestions please let me know.
Thanks Will P and all.
After working on a small similar Groovy DSL tool recently and digging a bit into griffon and swing and other material I currently ended with the following, that I submit as another suggestion to the answer.
Any comment/suggestion will be greatly appreciated.
IMHO a clear separation between the domain model (a Model class) , and the support classes (building, aggregation , context sharing, and filtering part of the model, at least a Builder class) is one of the key elements
to have a simple and efficient model. This is the pattern used in groovy swing and griffon and showed to be very flexible.
It allows constructs with each {}, with {} and collection operations which are clean and understandable enough,
and mostly without chaining operations (ie adding an annoying return this at the end of each operation).
Like here for example (source code at the end, MSys is a Facade to the underlying system):
MSys.with {
model.each {
it.sayHello
it.sayGoodBye
it.setAddress randomAddress(it.name);
it.sendEmail
}
println " Hello counts"
model.each {
def key = it.name
def value = MSys.context[key]
println "Counter for ${key} = ${value}"
}
}
or
MSys.model.each {
it.with {
sayHello
sayGoodBye
setAddress randomAddress(name) ;
sendEmail
}
}
or
MSys.eachModelDo {
sayHello
sayGoodBye
setAddress randomAddress(it.name);
sendEmail
}
or ...
(lot of possibilities)
It also allows to have some shared context very easily (in the example , context is a Map shared between all the Model elements where almost anything could be put, connection information, user preferences, cache etc.),
and to hide all the boiler plate to the user by putting it in another class/script .
The responsibilities would be :
SpecModel : Domain model : say hello, goodbye, properties etc
SpecBuilder : creates models (from a list in the example), holds shared context (a map) and eventually take care of the closure delegate context for some operations
This separation is important to deal with set operations on one side and entity (model) operations on the other.
Apart from beeing a builder, from a user POV it is a facade
and from a developer POV BTW it should be a Facade to several classes including the builder -but better start simple.
The next step would be to integrate FactoryBuilderSupport in this builder in order to benefit from Groovy DSL builder facilities, but this one big step beyond and I'm not comfortable with that yet ... (WIP right now)
Few Groovy recaps that you certainly already know but part of the problem if you want a light syntax :
In Groovy , , ; and () are optional except when there is a conflict.
Even where there is no direct conflict, the delegation strategy is not always direct and
run time errors are not always clear (see link on closures doc below)
Having a public getter method for properties, allows the use of property like access methods without the (),
the get and set prefixes are inferred
ie with a method like
public getSendEmail() {
println "email for ${name}"
}
you can use the syntax :
myObject sendEmail
Groovy assumes a property getter call and do the rest.
This helps removing the () in the dsl syntax.
The context is the delegate context of the closure it not the this
When you use closures, you use it to reference the object you are working on in the closure
(ie the receiver of the message).
Example:
[1, 2, 3].each { println it }
is ok
If you haved used
[1, 2, 3].each { println this }
you would have print a reference to the outer object, the console in groovy console
(Which IIRC was one of the problem you had in your first post)
This is well explained in the Groovy Closures doc :
(excerpt:)
this : the enclosing class where the closure is defined
owner : the enclosing object where the closure is defined, a class or a closure
delegate : third party object where methods calls or properties are resolved whenever the receiver of the message is not defined
The message delegation strategy (explained in the same document) is not always direct and this was your real problem I think.
That said, another key point in a DSL is the coherence from a user POV.
This means consistence in data access patterns and here the standard Groovy collections (Lists, maps each{} etc) can help a lot.
Plus it can be a huge plus for power users.
Syntactic sugar methods like eachModelDo can easily be done on a builder/facade class:
MSys.eachModelDo {
sayHello
sayGoodBye
setAddress randomAddress(it.name);
sendEmail
}
NB: eachModelDo is very simple but a bit tricky to debug
At one point it "worked" well without accessing correct variables :(
I have the feeling that something is wrong here (?) or at least it should be improved (comments welcome)
/**
* syntactic sugar
* direct access without 'it' (optional)
*/
public SpecBuilder eachModelDo(closure) {
model.each {
closure.delegate = it;
closure(it)
}
}
Bellow is the source code of a small test I did that you can cut and paste in a groovy console
The only part visible to the user should be the method Demo.run()
All the other stuff should be hidden
Any comments are welcome
/**
* The builder build Specs and defines utility methods, filters
* It shares its context with all elements in the domain
*/
class SpecBuilder {
/** the context will be shared with all domain model objects */
private context
/** model = all the domain model objects */
private model
/** getters / setters */
public getModel() { return model }
public getContext() {
return context
}
/** constructors and helpers */
public SpecBuilder(aContext) {
context = aContext
}
/** Default constructor forbidden */
private SpecBuilder() {}
public from(aList, closure) {
from(aList);
model.each { closure(it) }
return this
}
public from(aList) {
model = aList.collect { new SpecModel(it, context) }
return this
}
/* TODO filters etc */
/** stats: print counters */
public stats() {
println " Hello counts"
model.each {
def key = it.name
def value = this.context[key]
println "Counter for ${key} = ${value}"
}
}
/**
* syntactic sugar
* direct access without 'it' (optional)
*/
public SpecBuilder eachModelDo(closure) {
model.each {
closure.delegate = it;
closure(it)
}
}
}
/**
* The Spec Domain Model
*/
class SpecModel {
/** the shared context */
private context;
/** other properties */
private name;
public address;
/** getters and setters */
public getName() { return name }
public void setAddress(a) { address = a }
public getAddress() { return address }
public sayHello() { return getSayHello }
public sayGoodBye() { return getSayGoodBye }
public sendEmail() { return getSendEmail }
/** constructors */
public SpecModel(aName, aContext) {
name = aName
context = aContext
}
/** Default constructor forbidden */
private SpecModel() {}
/** method used like properties, without 'get' and ()*/
public getSayHello() {
println "(!) hello ${name}"
context[name] = context.get(name,0) +1;
}
public getSayGoodBye() {
println "goodBye ${name} !"
}
public getSendEmail() {
println "email for ${name}"
if (address)
println "Address ${address}"
}
public getPrintContext() {
println context
}
/**
* Returns info to caller
*/
public gatherInfo() {
"info for ${name} : ${new java.util.Random().nextInt(50000)}"
}
}
class Demo {
// several Groots here to test uniques ...
def customers = ['Groot', 'Gamora', 'Groot', 'Groot', 'Groot', 'Star-Lord']
/**
* Utility function who generates a random address
* #param name will prefix the address
* #return the address
*/
public randomAddress(def name) {
// good places ... :)
def places = [
"Grande Rue",
"Cours Emile Zola",
"Place Antonin Poncet",
"Rue de la République",
"Boulevard de la Croix Rousse",
"Place Bellecour"
]
def random = new java.util.Random();
return new StringBuilder().append(name).append(" ... ")
// why not 42?
.append( random.nextInt(155)).append(" ")
.append( places[random.nextInt(places.size())] )
.toString();
}
/**
* ======================== Main user program =========================
*/
def run() {
/** the shared context */
def context = [:].asSynchronized() // In case of multi threading access
/** The whole system From a user POV : a big façade */
def MSys = new SpecBuilder(context).from(customers) ;
println "*** 1 ==================== "
/** First form */
MSys.model.each {
it.with {
sayHello
sayGoodBye
setAddress randomAddress(name) ;
sendEmail
}
}
/** other forms
* MSys.with{ is handy
* one could write MSys... on each line
*/
MSys.with {
println "*** 2 ==================== "
model.each { it.sayHello };
println "*** 3 ==================== "
model.with { println " a Model entry = ${it.name} + ${it.address}" }
println "*** 4 ==================== "
/** false not to mutate the model !!! */
model.unique(false, { a, b -> a.name <=> b.name }).each { it.sayHello }
println "*** 5 ==================== "
context['aKey'] = 42
// verify that each entity has the same context
model.with { println " a shared context for ${it.name} : " + it.context }
println "*** Stats ================ "
/** stats on the shared context */
stats()
}
println "*** 6 Info to process ======== "
/** Gather info to process (addresses)*/
def data = MSys.model.inject([:]) { result, entity ->
result[entity.name] = entity.address
result
}
println data
MSys.with {
println "*** 7 ==================== "
model.each {
it.sayHello
it.sayGoodBye
it.setAddress randomAddress(it.name);
it.sendEmail
}
println "*** 8 ==================== "
println " Hello counts"
model.each {
def key = it.name
def value = MSys.context[key]
println "Counter for ${key} = ${value}"
}
}
println "*** 9 ==================== "
MSys.eachModelDo {
sayHello
sayGoodBye
setAddress randomAddress(it.name);
sendEmail
}
}
}
new Demo().run()
/* end of script */

Unit test Groovy2.0 with Spock : setup( )

I am writing unit test using Spock for groovy-2.0 , and using gradle to run. If I write following the test pass.
import spock.lang.Specification
class MyTest extends Specification {
def "test if myMethod returns true"() {
expect:
Result == true;
where:
Result = new DSLValidator().myMethod()
}
}
myMethod() is a simple method in DSLValidator class, that simply returns true.
But if I write a setup() function and create the object in setup(), my test fails: Gradel says: FAILED: java.lang.NullPointerException: Cannot invoke method myMethod() on null object
Following is what it looks like with setup(),
import spock.lang.Specification
class MyTest extends Specification {
def obj
def setup(){
obj = new DSLValidator()
}
def "test if myMethod returns true"() {
expect:
Result == true;
where:
Result = obj.myMethod()
}
}
Can somebody help?
Here is the solution I got to the problem:
import spock.lang.Specification
class DSLValidatorTest extends Specification {
def validator
def setup() {
validator = new DSLValidator()
}
def "test if DSL is valid"() {
expect:
true == validator.isValid()
}
}
In Spock objects stored into instance fields are not shared between feature methods. Instead, every feature method gets its own object.
If you need to share an object between feature methods, declare a #Shared field.
class MyTest extends Specification {
#Shared obj = new DSLValidator()
def "test if myMethod returns true"() {
expect:
Result == true
where:
Result = obj.myMethod()
}
}
class MyTest extends Specification {
#Shared obj
def setupSpec() {
obj = new DSLValidator()
}
def "test if myMethod returns true"() {
expect:
Result == true
where:
Result = obj.myMethod()
}
}
There are 2 fixture methods for setting up the environment:
def setup() {} // run before every feature method
def setupSpec() {} // run before the first feature method
I don't understand why the second example with setupSpec() works and fails with setup() because in documentation says otherwise:
Note: The setupSpec() and cleanupSpec() methods may not reference
instance fields.

Resources