I try to keep up with developments in the Groovy and Grails worlds. I really do. I follow most of the core team members on Twitter. I listen to the Grails Podcast when I can. I go to many conferences and attend other talks when I’m not speaking. I try to follow the email lists, though they’re way too high volume. I even have a Google+ account, though I don’t check it very often.
It’s all too much, actually. As I get older, I find that keeping up isn’t just a question of time, it’s a question of energy. Sometimes I’ll manage to catch up on my Twitter feed and am too tired to do much else.
So new developments slip by me. I guess that’s good, since it’s indicative of an active ecosystem, but it can be frustrating at times.
The one that forms the subject of this blog post is pretty trivial and arguably not worth writing about, but I missed it when it first came out so I thought I’d document it here.
Say you have a script in Groovy and you want to test it. You can execute the script programmatically using GroovyShell
, and supply any needed variables with an instance of the Binding
class.
Here’s a trivial example. I have the following massively complex, powerful script:
[sourcecode language=”groovy”]
z = x + y
[/sourcecode]
Say this is saved in a file called ‘math.groovy
‘. Since none of the variables x
, y
, and z
are declared at all in the script (not even using def
), they can be accessed through a Binding
object.
[sourcecode language=”groovy”]
Binding binding = new Binding()
binding.x = 3; binding.y = 4
GroovyShell shell = new GroovyShell(binding)
shell.execute(new File(‘math.groovy’))
assert 7 == binding.z
[/sourcecode]
This is easy enough to convert into a test case, as a subclass of GroovyTestCase
.
[sourcecode language=”groovy”]
class ScriptTests extends GroovyTestCase {
void testMath() {
Binding binding = new Binding()
binding.x = 3; binding.y = 4
GroovyShell shell = new GroovyShell(binding)
shell.evaluate(new File(‘math.groovy’))
assertEquals 7, binding.z
}
}
[/sourcecode]
By extending GroovyTestCase
, this entire script can be executed at the command line or inside the Groovy Console without adding any additional library dependencies of any kind (not even JUnit). When I ran it, the result was:
.
Time: 0.037
OK (1 test)
That’s all well and good. I was writing up examples like this for my book (Making Java Groovy, available through the Manning Early Access Program at http://manning.com/kousen) and decided to look at the Groovy API for GroovyTestCase
. Lo and behold, what do I stumble upon but a class called GroovyShellTestCase
. The class was authored by Alex Tkachman (so you know it’s good :)), presumably back in 2008 if the copyright statement is to be believed. Of course, the copyright could just be a copy-and-paste issue*.
*Which is what I like to call the CAP Design Pattern — how to take an error in one small part of your system and distribute it throughout the entire code base.
The GroovyDocs are a bit thin on that class, but they say (and I quote), “GroovyTestCase, which recreates internal GroovyShell in each setUp()”. The class has a protected field called shell
of type GroovyShell
, and a method called withBinding
with a couple of overloads. For my purposes, I want the overload that takes two arguments, the first being a Map
of binding variables, and the second being a closure to be executed. The method executes the closure with the given binding:
[sourcecode language=”groovy”]
protected def withBinding(Map map, Closure closure)
[/sourcecode]
Therefore, if I extend GroovyShellTestCase
, I can rewrite my test as:
[sourcecode language=”groovy”]
class ScriptTests extends GroovyShellTestCase {
void testMath() {
def result = withBinding( [x:3, y:4] ) {
shell.evaluate(new File(‘math.groovy’))
shell.context.z
}
assertEquals 7, result
}
}
[/sourcecode]
The map supplies x
and y
to the binding, which is automatically supplied to the instantiated shell
. I can get the result by calling the getContext
method on the shell (i.e., access the context
property) which returns the binding, and then accessing its z
property. I’m taking advantage of the fact that the implementation of the withBinding
method includes “return closure.call()
“, so the last evaluated expression in the closure is returned automatically.
This isn’t a huge deal, but it’s there and presumably it’s been there for a while and I never knew it. Now I know. Even better, now you know. Maybe you’ll have a use case that needs it. I’ve been trying to make sure that all my scripts in my book have test cases, and this gives me a convenient way to write them.
My only disappointment in discovering this is that when I looked for the tests for the GroovyShellTestCase
class, I didn’t find any. In the Groovy distribution under src/test/groovy/util
, there’s GroovyScriptEngineTest
, GroovyTestCaseTest
, and even GroovySwingTestCase
, but no dedicated class for testing GroovyShellTestCase
. Maybe that’s my opportunity to add one and make a (tiny, but useful) contribution to the language. That would be fund to do just to write a class called GroovyShellTestCaseTest
. Heck, that’s only one small step away from the palindromic GroovyShellTestCaseTestShell.groovy
. 🙂
Leave a Reply