Categories
Groovy

Testing Groovy Scripts

I often start Groovy development with scripts rather than classes. Scripts are quick and easy, both to write and to run, and the feeling of having only a few lines of code makes me more willing to experiment. Most of my scripts eventually turn into classes, but once in a while, either for convenience or as demos, they stay the way they are.

This happens a lot while I’m working on my book*. I often generate little scripts that illustrate one point or another, and most of them aren’t worth converting into classes.

(*What book is that, you say? Why, Making Java Groovy, which shows you how to add Groovy to Java to make your development tasks easier. It’s available now from the Manning Early Access Program at http://manning.com/kousen)

While I’m not sure I practice true Test Driven Development (TDD), I know I practice GDD. That’s Guilt Driven Development, which means if I write anything significant that isn’t tested I feel guilty about it, so I then write the tests. In this post I’ll show how I now write tests for my Groovy scripts.

Before I do so, however, I should acknowledge the assistance of the indefatigable Hamlet D’Arcy on the Groovy Users email list. He blogged about a similar issue back in 2006 (!). Of course, he was dealing with straight Java back then and trying to manage standard error.

There are two features I need to use:

  • The groovy.lang.GroovyShell and groovy.lang.Binding classes, and
  • Overriding System.out to capture printed output

I use a GroovyShell to execute the scripts, and a Binding to manage input and output variables, if any. To handle printed output, I redirect standard output to a ByteArrayOutputStream and then check the results.

To illustrate, here are three extremely powerful scripts. The first is the classic “Hello, World!” script in Groovy. I stored it in a file called hello_world.groovy.

[sourcecode lang=”groovy”]
println ‘Hello, World!’
[/sourcecode]

Second, here is a script that contains an assert in it, in a file I called script_with_assert.groovy.

[sourcecode lang=”groovy”]
def ok = true
assert ok
[/sourcecode]

I used a local variable, called ok, in that script, mostly to contrast it with a binding variable. Speaking of which, here’s my super duper script that has a binding variable in it, stored in a file called script_with_variable.groovy.

[sourcecode lang=”groovy”]
package mjg.scripts
ok
[/sourcecode]

Recall from the fantastic book Groovy in Action* (even after all these years and language changes, I still find valuable information in there) that if a variable in a script is not declared, then it can be set and retrieved via an instance of groovy.lang.Binding.

 

(*Yeah, I linked to the second edition, even though I’m still using the first edition on a regular basis. That’s still my all-time favorite technical book, so I don’t mind recommending the new version.)

With all that in mind, here’s my JUnit 4 test, written in Groovy for convenience.

[sourcecode lang=”groovy”]
package mjg.scripts

import static org.junit.Assert.*

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

class ScriptTests {
GroovyShell shell
Binding binding
PrintStream orig
ByteArrayOutputStream out

@Before
void setUp() {
orig = System.out
out = new ByteArrayOutputStream()
System.setOut(new PrintStream(out))
binding = new Binding()
shell = new GroovyShell(binding)
}

@After
void tearDown() {
System.setOut(orig)
}

@Test
void testScriptWithAssert() {
shell.evaluate(new File("src/mjg/scripts/script_with_assert.groovy"))
}

@Test
void testScriptWithTrueVariable() {
binding.ok = true
shell.evaluate(new File("src/mjg/scripts/script_with_variable.groovy"))
assertTrue shell.ok
}

@Test
void testScriptWithFalseVariable() {
binding.ok = false
shell.evaluate(new File("src/mjg/scripts/script_with_variable.groovy"))
assertFalse shell.ok
}

@Test
void testHelloWorld() {
shell.evaluate(new File("src/mjg/scripts/hello_world.groovy"))
assertEquals "Hello, World!", out.toString().trim()
}

}
[/sourcecode]

Actually, all my scripts (and the test class) are in a package called mjg.scripts, where mjg stands for Making Java Groovy, of course.

 

I made the GroovyShell and the Binding instances attributes so I could reuse them. It turns out that by keeping a reference to the Binding, I can still set and get variables using it even though I’ve already instantiated by Groovy shell around it. That’s helpful.

In the set up method (annotated with @Before), I save the current output stream into an attribute so I can restore it later in the tear down method (annotated with @After). That’s not really necessary here, but it seems like a good practice in general.

I’m using a ByteArrayOutputStream to hold the printed data in memory. The System.setOut method requires a PrintStream as an argument, so I wrapped the byte stream inside one.

The first test, testScriptWithAssert, finds the proper file and executes it. That script has the line assert ok in it, after setting the local variable ok to true. If I go into the script and change ok to false, the test fails. This tells me two things:

  1. If my scripts are full of assert statements, I can run them as part of a normal build and failures will be reported in the usual way.
  2. If an assert fails, there isn’t much I can do to prepare for it in my tests. I can’t, for example, wrap the call inside an assertFalse call. The failure happens before I get back, for one thing.

I basically have to hope that any assert statements in my scripts always succeed, or my tests will fail. Arguably that’s what I want anyway.

The next two tests, testScriptWithTrueVariable and testScriptWithFalseVariable, set the ok variable from the binding, then run the script. The script in question just returns the variable. I check the binding variable at the end of each script, just to prove that I was able to set it properly with the binding.

Finally, I test the “Hello, World!” script. By redirecting standard out to the byte stream, I’m able to capture the print output in a variable. I then invoke the toString method to get the value in the buffer. The trim call is added to handle any whitespace associated with carriage returns, line feeds, etc.

In his blog post, Hamlet used an encoding as the argument to his toString call, claiming that otherwise the output would be different on different platforms. I decided to let Groovy worry about that, so hopefully I won’t get burned by it later.

That’s all there is to it. Now, whenever I add Groovy scripts to a chapter of my book, I make sure to add a test class to check them as well. So far that seems to be working just fine.

Any comments, especially about errors or omissions, are of course welcome. For those who have already purchased the book, (1) you totally rock, and (2) a new chapter will be added to the MEAP early next week, on using Groovy with SOAP-based web services. I’ll blog about that when the new chapter appears.

Note: the source code for this project, including a Gradle build file, is located in my TestingGroovyScripts repository at GitHub.

By Ken Kousen

I am a Java Champion and the author of the books "Kotlin Cookbook", (O'Reilly Media), "Modern Java Recipes" (O'Reilly Media), "Gradle Recipes for Android" (O'Reilly Media), and "Making Java Groovy" (Manning), as well as over a dozen video courses at Safari Books Online. I'm a regular member of the No Fluff, Just Stuff conference tour and have given talks all over the world. Through my company, Kousen IT, Inc, I've taught training courses to and worked with thousands of developers.

8 replies on “Testing Groovy Scripts”

Actually, instead of redirecting System.out, you should set the “out” variable in the binding, which is used by printle. So you could avoid encoding issues, avoid redirecting streams, by doing something like this:

def content = new StringWriter()
binding.out = new PrintWriter(content)

println “hello”

assert content.toString() == “hello\n”

You know, that’s why I blog about these things. It’s only partly to share what I’ve learned. More importantly, it’s to give people like you an opportunity to show me what I didn’t know in the first place. 🙂

Thanks for this. I’ll definitely revise my tests to match this approach.

FYI, the “ok” is not appearing in the script_with_variable.groovy code. Actually, it looks like it shows up for a split second, and then the code formatting wipes it out so it’s all empty. At least it does in Chrome and Opera.

Also, could you post an updated Test file, just so I’m sure I followed Laforge’s advice properly? Thanks!!

That’s weird about the ok vanishing in the blog post. I’ll try adding a comment and see if that helps.

The test case at GitHub is already updated to use Guillaume’s approach. See the code at https://github.com/kousen/TestingGroovyScripts . Even if you’re not a git user, you can browse the code online. I made the changes he suggested right away. When Guillaume Laforge tells me something, I listen. 🙂

The ok is now ok 🙂

I tried that StringWriter approach myself, in my hellow_world.groovy script I typed println "hello world" two times. But I wasn’t getting a good assert comparison. However, I changed the code to print "hello world\n" twice, and everything asserted perfectly.
In other words, this assert line assertEquals response.toString().trim(), 'hello world\nhello world\n' works with two print’s in the script, but not two println’s.
Any ideas what I’m doing wrong?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.