I found an interesting line in Spock interactions documentation:
http://spockframework.org/spock/docs/1.3/interaction_based_testing.html#_argument_constraints
last line of constraints, example with closure predicate:
1 * subscriber.receive({ it.size() > 3 && it.contains('a') })
My question is: is there a way in Groovy to pass this predicate as variable?
My testing environment code:
class Something {
Doer doer
Something(Doer doer) {
this.doer = doer
}
boolean doSth(x) {
if (!doer.validate(x)) throw new RuntimeException()
true
}
}
class Doer {
boolean validate(int x) {
x == 2
}
}
and test code:
def "some test"() {
given:
def d = Mock(Doer)
def s = new Something(d)
when:
s.doSth(2)
then:
1 * d.validate({ it == 2 }) >> true
}
what I would like to achieve:
def "some test"() {
given:
def d = Mock(Doer)
def s = new Something(d)
def myClosureVar = { ??? }
when:
s.doSth(2)
then:
1 * d.validate(myClosureVar) >> true
}
The closure takes an argument, as indicated by it having a defined value. That value is the corresponding method parameter. So whatever closure you define outside of your interaction, you need to make sure that the interaction hands over that parameter to the closure, i.e. you need to create your own (small and simple) closure evaluating the outer (potentially lengthier, more complex) closure with the parameter it:
1 * d.validate({ myClosureVar(it) }) >> true
Sorry for the repetition, but I always prefer a full MCVE in my answers so you can easily copy, paste, compile and run:
Application classes:
package de.scrum_master.stackoverflow.q60341734
class Doer {
boolean validate(int x) {
x == 2
}
}
package de.scrum_master.stackoverflow.q60341734
class Something {
Doer doer
Something(Doer doer) {
this.doer = doer
}
boolean doSth(x) {
if (!doer.validate(x)) throw new RuntimeException()
true
}
}
Spock specification:
package de.scrum_master.stackoverflow.q60341734
import org.junit.Rule
import org.junit.rules.TestName
import spock.lang.Specification
class SomethingTest extends Specification {
#Rule
TestName testName
def "some test"() {
given:
def d = Mock(Doer)
def s = new Something(d)
when:
s.doSth(2)
then:
1 * d.validate({ println "$testName.methodName: closure parameter = $it"; it == 2 }) >> true
}
def "another test"() {
given:
def d = Mock(Doer)
def s = new Something(d)
def myClosureVar = { println "$testName.methodName: closure parameter = $it"; it == 2 }
when:
s.doSth(2)
then:
1 * d.validate({ myClosureVar(it) }) >> true
}
}
Console log:
some test: closure parameter = 2
another test: closure parameter = 2
Related
How can I count closure method calls.
I have a closure as below:
class SomeTestSpec extends Specification {
def greeting = { "Hello world!" }
#Unroll
def "test description"() {
given: // ….
when:
def v = greeting then: // need something like that: 1 * greeting
}
}
I call it once in the when: clause.
How can I test the call count (one in my case) in the then: clause.
In Groovy Unit Test with Spock the following task is quite common:
assert myResult == calculateExpectedResult() (With or without the assert keyword.)
The groovy assert prints out lots of infomation on what is going on here and why my assertion failed. But when the compared objects are very complex and deep it can be tricky go get the concrete property that failed the test.
For this I found the Javers Framework that does an excellent Job comparing the objects and producing an exact diff. I created a trait to do this:
trait DiffTrait {
Javers javers = JaversBuilder.javers().build()
String diff(result, expected) {
Diff diff = javers.compare(result, expected);
def valueChanges = diff.getChangesByType(ValueChange)
String message = ""
valueChanges.each { message += "\n$it.propertyName = $it.left instead of expected: $it.right" }
return message
}
}
Now I can use it in my Unit Tests like this:
def expected = calculateExpectedResult()
assert myResult == expected, diff(myResult, expected)
This way I get a nicely printed list of differences.
But this is kind of verbose because I have to specify the values two times.
So I have changed the trait like this:
trait DiffTrait {
Javers javers = JaversBuilder.javers().build()
def result
def expected
String diff(result, expected) {
Diff diff = javers.compare(result, expected);
def valueChanges = diff.getChangesByType(ValueChange)
String message = ""
valueChanges.each { message += "\n$it.propertyName = $it.left instead of expected: $it.right" }
return message
}
String diff() {
diff(result, expected)
}
def result(result) {
this.result = result
return result
}
def expected(expected) {
this.expected = expected
return expected
}
}
The idea was to use it like this:
def result = callTheSystemToProduceTheRealResult()
def expected = calculateExpectedResult()
assert result(myResult) == expected(expected), diff()
But surprisingly this does not work! The two attributes are null and the diff Method fails with a NotNull-Exception. If I debug this code the expected/result methods are never called!
If I rewrite the code like this
def result = result(callTheSystemToProduceTheRealResult())
def expected = expected(calculateExpectedResult())
assert myResult == expected, diff()
everything works as expected. The methods get called correctly and the attributes are set.
My question is: Why can't I call these methods in the assert statement? What is the difference from the Groovy/Spock perspective of these two code fragements?
Here is a gist containing all the code as running example.
It is quite easy to explain. Assertion message is evaluated before the assertion itself. The following piece of code works perfectly, however it displays static diff message:
import org.javers.core.Javers
import org.javers.core.JaversBuilder
import org.javers.core.diff.Diff
import org.javers.core.diff.changetype.ValueChange
import spock.lang.Specification
class LolSpec extends Specification implements DiffTrait {
def 'lol'() {
expect:
def whatIGot = new Lol(l: 'a')
def whatIExpected = new Lol(l: 'b')
assert result(whatIGot) == expected(whatIExpected), 'diff'
}
}
trait DiffTrait {
Javers javers = JaversBuilder.javers().build()
def result
def expected
String diff() {
diff(result, expected)
}
String diff(result, expected) {
Diff diff = javers.compare(result, expected);
def valueChanges = diff.getChangesByType(ValueChange)
String message = ""
valueChanges.each { message += "\n$it.propertyName = $it.left instead of expected: $it.right" }
return message
}
def result(result) {
this.result = result
return result
}
def expected(expected) {
this.expected = expected
return expected
}
}
class Lol {
String l
}
You need to pass the arguments twice or change the implementation, e.g.:
import groovy.transform.EqualsAndHashCode
import org.javers.core.Javers
import org.javers.core.JaversBuilder
import org.javers.core.diff.changetype.ValueChange
import spock.lang.Specification
class LolSpec extends Specification {
def 'lol'() {
expect:
def whatIGot = new Lol(l: 'a')
def whatIExpected = new Lol(l: 'b')
def diff = new Diff(result: whatIGot, expected: whatIExpected)
assert diff.check(), diff.message()
}
}
class Diff {
Javers javers = JaversBuilder.javers().build()
def result
def expected
String message() {
def diff = javers.compare(result, expected);
def valueChanges = diff.getChangesByType(ValueChange)
String message = ""
valueChanges.each { message += "\n$it.propertyName = $it.left instead of expected: $it.right" }
return message
}
boolean check() {
result.equals(expected)
}
}
#EqualsAndHashCode
class Lol {
String l
}
Let's say that I have a collection of parameters
def params = ['a','b','c']
Is there a short way to run a method that accepts a single parameter once for every element of a collection to replace this:
params.each {
foo(it)
}
with something more declarative (like a "reverse" spread operator)?
You can use collect:
def params = ['a','b','c']
def foo(param) {
'foo-' + param
}
assert ['foo-a', 'foo-b', 'foo-c'] == params.collect { foo(it) }
Or just a closure
def foo = { a -> a + 2 }
def modified = list.collect foo
You can use method pointer:
def l = [1,2,3]
l.each(new A().&lol)
class A {
def lol(l) {
println l
}
}
Or add a method that will do the task you need:
def l = [1,2,3]
List.metaClass.all = { c ->
delegate.collect(c)
}
l.all(new A().&lol)
class A {
def lol(l) {
println l
return l+2
}
}
Let's say I have a DSL like this
setup {name = "aDSLScript"}
println "this is common groovy code"
doStuff {println "I'm doing dsl stuff"}
One would have a delegating class implementing the methods 'setup' and 'doStuff' usually. Beside, one could write common Groovy code to be executed (println...).
What I am searching for, is a way to execute this in two steps. In the first step only the setup method should be processed (neither println). The second step handles the other parts.
At the moment, I have two delegating classes. One implements 'setup' the other one implements 'doStuff'. But both execute the println statement, of course.
You can create a single class to intercept the method calls from the script and let it coordinate the following method invoke. I did it through reflection, but you can go declarative if you want. These are the model and script classes:
class FirstDelegate {
def setup(closure) { "firstDelegate.setup" }
}
class SecondDelegate {
def doStuff(closure) { "secondDelegate.doStuff" }
}
class MethodInterceptor {
def invokedMethods = []
def methodMissing(String method, args) {
invokedMethods << [method: method, args: args]
}
def delegate() {
def lookupCalls = { instance ->
def invokes = instance.metaClass.methods.findResults { method ->
invokedMethods.findResult { invocation ->
invocation.method == method.name ?
[method: method, invocation: invocation] : null
}
}
invokes.collect { invoked ->
invoked.method.invoke(instance, invoked.invocation.args)
}
}
return lookupCalls(new FirstDelegate()) + lookupCalls(new SecondDelegate())
}
}
Here be scripts and assertions:
import org.codehaus.groovy.control.CompilerConfiguration
def dsl = '''
setup {name = "aDSLScript"}
println "this is common groovy code"
doStuff {println "Ima doing dsl stuff"}
'''
def compiler = new CompilerConfiguration()
compiler.scriptBaseClass = DelegatingScript.class.name
def shell = new GroovyShell(this.class.classLoader, new Binding(), compiler)
script = shell.parse dsl
interceptor = new MethodInterceptor()
script.setDelegate interceptor
script.run()
assert interceptor.invokedMethods*.method == [ 'setup', 'doStuff' ]
assert interceptor.delegate() ==
['firstDelegate.setup', 'secondDelegate.doStuff']
Notice I didn't bothered intercepting println call, which is a DefaultGroovyMethods thus, a little more cumbersome to handle.
Also having the class MethodInterceptor implementing the method delegate() is not a good idea, since this allows the user-defined script to call it.
I found a way to split up execution of the DSL script. I used a CompilationCustomizer to remove every statement from AST except the doFirst{}. So the first run will only execute doFirst. The second run does everything else. Here's some code:
class DoFirstProcessor {
def doFirst(Closure c) {
c()
}
}
class TheRestProcessor {
def doStuff(Closure c) {
c()
}
def methodMissing(String name, args) {
//nothing to do
}
}
def dsl = "
println 'this is text that will not be printed out in first line!'
doFirst { println 'First things first: e.g. setting up environment' }
doStuff { println 'doing some stuff now' }
println 'That is it!'
"
class HighlanderCustomizer extends CompilationCustomizer {
def methodName
HighlanderCustomizer(def methodName) {
super(CompilePhase.SEMANTIC_ANALYSIS)
this.methodName = methodName
}
#Override
void call(SourceUnit sourceUnit, GeneratorContext generatorContext, ClassNode classNode) throws CompilationFailedException {
def methods = classNode.getMethods()
methods.each { MethodNode m ->
m.code.each { Statement st ->
if (!(st instanceof BlockStatement)) {
return
}
def removeStmts = []
st.statements.each { Statement bst ->
if (bst instanceof ExpressionStatement) {
def ex = bst.expression
if (ex instanceof MethodCallExpression) {
if (!ex.methodAsString.equals(methodName)) {
removeStmts << bst
}
} else {
removeStmts << bst
}
} else {
removeStmts << bst
}
}
st.statements.removeAll(removeStmts)
}
}
}
}
def cc = new CompilerConfiguration()
cc.addCompilationCustomizers new HighlanderCustomizer("doFirst")
cc.scriptBaseClass = DelegatingScript.class.name
def doFirstShell = new GroovyShell(new Binding(), cc)
def doFirstScript = doFirstShell.parse dsl
doFirstScript.setDelegate new DoFirstProcessor()
doFirstScript.run()
cc.compilationCustomizers.clear()
def shell = new GroovyShell(new Binding(), cc)
def script = shell.parse dsl
script.setDelegate new TheRestProcessor()
script.run()
I did another variation of this where I execute the DSL in one step. See my blog post about it: http://hackserei.metacode.de/?p=247
A class implements call method so that it's objects can be called as a method. This works for most of the case but not when the call is being made inside a closure on a object which is instance variable of a class.
To demonstrate the problem, in the code below I've commented the interesting lines with numbers. While most variants result in same output, only the line with comment 5 doesn't work. It throws groovy.lang.MissingMethodException: No signature of method: Client2.instanceVar() is applicable for argument types: () values: [])
Can someone help me understand the reason? Is it a bug?
class CallableObject {
def call() { println "hello" }
}
class Client {
def instanceVar = new CallableObject()
def method() {
def localVar = new CallableObject()
def closure1 = { localVar() }
def closure2 = { instanceVar.call() }
def closure3 = { instanceVar() } // doesn't work
localVar() // 1
instanceVar() // 2
closure1() // 3
closure2() // 4
closure3() // 5
}
}
new Client().method()
I guess this will make it clear.
class CallableObject {
def call() { println "hello" }
}
class Client {
def instanceVar = new CallableObject()
def getInstanceVar() {
println "Getter Called"
instanceVar
}
def method() {
def localVar = new CallableObject()
def closure1 = { localVar() }
def closure2 = { instanceVar.call() }
def closure3 = { this.#instanceVar() } //should work now
localVar() // 1
instanceVar() // 2
closure1() // 3
closure2() // 4
closure3() // 5
}
}
new Client().method()
You will see "Getter Called" printed when closure2() invoked. For a global property to be accessed in the closure inside a method, the getter in called instead. To surmount the error you get, the field instanceVar needs to be accessed directly in order to implicitly use call().