23 Jun 2012 Unit Testing with Groovy
This post is about unit testing with Groovy. Groovy was the first language I used after Java. It lets me do a lot of the things I do with Java, but more quickly. Like many developers who are using another language after Java for the first time – whether it be Ruby, Scala, Groovy or something else – I’ve quickly come to love it.
In this post I’m going to show you how Groovy can make your tests more succinct. We’ll focus on syntax sugar for creating and consuming Maps and Lists, which is something you do a lot of when preparing data for a unit test. Than I’ll give a quick introduction to closures, which will lead us into some of the techniques we can use for mocking in Groovy.
So why would you want to consider Groovy?
The first thing you should know about Groovy is that it is very similar to Java. About 90% of Java code pasted into a Groovy file will work unchanged, and it only takes pretty small changes to make the remaining stuff work. As a result, it’s not too hard to move a Java team over to Groovy.
The chief benefit you get from using Groovy is a cleaner syntax. Things that take a long time to type out in Java – such as defining lists and maps – are gone, thanks to cleaner syntax.
To change your build to use Groovy is quite easy. For example in Ant, you add the Groovy jar to the classpath and change the javac task to groovyc instead. groovyc will build both Java and Groovy and link between them. There are also similar tools for Maven users.
An important point to remember is that you don’t need to rewrite your old code. Groovy compiles down to bytecode so Java can call Groovy methods and vice-versa. You can still use all your existing Java projects and libraries with Groovy, and in some cases you can enhance them too.
Collections
Let’s now look into how Groovy can make it easier for you to set up data in your tests.
Lists
One of the most commonly used shortcuts you’ll use in your tests is the shorthand for Lists. You can create an ArrayList
by wrapping the elements in brackets and separating with a comma:
List days = [ "Monday", "Tuesday", "Wednesday" ]
Because Groovy is a dynamic language, you don’t even need to declare the type at the front – you can use the def
keyword (notice also that the semicolon at the end is optional):
def days = ["Monday", "Tuesday", "Wednesday” ]
If you’d like to create something other than an ArrayList
, you can use the as
keyword to cast the type to something else. So if I want it to be an array of strings instead of a list, I do this:
def days = ["Monday", "Tuesday", "Wednesday” ] as String[]
For accessing elements in the list, instead of get()
, you can use square brackets:
println days[1] // days.get(1) Tuesday
But why stop there? You can use negative indexes to return the elements from the end of the collection:
println days[-1] Wednesday
You can get also get a sublist by specifying a range:
println days[1..2] [Tuesday, Wednesday]
or return a range using the negative indexes of a list:
println days[-1..-2] [Wednesday, Tuesday]
Maps
For maps, the syntax is one step up from a list. Each key-value pair is separated by a colon:
def aussieCapitals = [ Victoria : ‘Melbourne’, Queensland: ‘Brisbane’]
To get the element in the list, I use the same syntax for a list, except now the key is a string, not an integer:
aussieCapitals[‘Victoria’] ‘Melbourne’
BTW, you may have noticed I’m using single quotes instead of double quotes – that’s another Groovy shorthand.
So let’s now compare the verbosity of Java:
Map aussieCapitals = new HashMap(); aussieCapitals.put("Victoria", "Melbourne"); aussieCapitals.put("Queensland", "Brisbane"); aussieCapitals.put("New South Wales", "Sydney"); aussieCapitals.put("Tasmania", "Hobart"); aussieCapitals.put("Australian Capital Territory", "Canberra"); aussieCapitals.put("South Australia", "Adelaide"); aussieCapitals.put("Northern Territory", "Darwin"); aussieCapitals.put("Western Australia", "Perth"); assert ("Perth".equals(aussieCapitals.get("Tasmania")));
with that of the equivalent Groovy:
def aussieCapitals = [Victoria: 'Melbourne', Queensland: 'Brisbane', 'New South Wales': 'Sydney', Tasmania: 'Hobart', 'Australian Capital Territory': 'Canberra', 'South Australia':"Adelaide", "Northern Territory": "Darwin", "Western Australia": 'Perth'] assert "Hobart"==aussieCapitals['Tasmania']
You can see that it’s a lot easier to read a Groovy test than it is to read Java.
Closures
So let’s now briefly digress into closures. Putting aside the strict computer-science definition, for our purposes closures can be thought of as functions that can be passed around as method arguments and then executed in different contexts. You may have come across them before in Javascript.
Most Java developers would have used anonymous inner classes at some point whilst working with Swing UI, threading, etc. These bear the closest resemblance to closures in Groovy.
Consider the following example of a weekdays list. Groovy has added a method to List called each
, which takes a closure (the thing in the curly braces) and performs that function on each of the elements. In our example below, the String
is a parameter to the closure function that we’ve defined. The arrow indicates the beginning of the actual function. In this instance, the closure just calls println
to print out each element:
def weekdays = [‘Monday’, ‘Tuesday’, ‘Wednesday’, ‘Thursday’, ‘Friday’] weekdays.each { String it -> println """The day is ${it}""" } The day is Monday The day is Tuesday …
Incidentally, notice in the println
that I’m printing out a formatted string using the Groovy string syntax – it’s very similar to the expression language (EL) format if you’ve done any work with JSF or Spring.
If we want to use closures on maps as well, we can. This time, I’m feeding two variables into the closure method:
aussieCapitals.each { key, value -> println """The capital of ${key} is ${value}""" } -> The capital of Victoria is Melbourne
This prints out the key-value pair. Notice that the type is optional – it’s like implicitly having the def
keyword in there.
The other method I’m going to show you is collect
. All this does is iterate through the collection, perform the method on each of the elements, and return the output as a new collection:
aussieCapitals.collect {k, v -> return v.toUpperCase() } [‘MELBOURNE’,’BRISBANE’,…]
Unit Testing Support in Groovy
Groovy includes some excellent new tools for unit testing: a new assertion method, a JUnit 3 test harness called GroovyTestCase
, and a mocking framework that provides a mechanism for mocking with closures. We’ll discuss some of them here.
Power Asserts
Groovy comes with an assert
statement built into the language that is able to introspect objects and provide a more descriptive output about when an assertion fails.
As you may already know, a regular assert that looks like this:
assert (Arrays.asList("Monday","Tuesday","Wed").contains("Friday"))
will throw you a pretty boring assertion error in Java:
Exception in thread "main" java.lang.AssertionError at AssertDemo.main(AssertDemo.java:5)
In Groovy the output is considerably better, showing the evaluation of all the statements elements:
Furthermore, unlike assertions in Java, you don’t need to specify the –ea
flag to catch a Groovy assert.
MockFor and StubFor
This is Groovy’s built-in mocking framework and it’s a great example of some real ‘groovy’ features put to good use:
void testMockOutTheWeatherClass() { def weatherMock = new MockFor(WeatherReport) weatherMock.demand.with { predictWeather { Weather w-> "Injected Code" } yearlyMeanTemp { 17.5 } } weatherMock.use { def report = new WeatherReport() def prediction = report.predictWeather() assert "Injected Code" == prediction assert report.yearlyMeanTemp() == 17.5 // an automatic 'verify' happens at the end of the closure } }
You can see that we specify the class to mock in the MockFor
constructor.
MockFor
will add a property, demand
, that we then use to dictate what the mock should return, and in what order. The with
closure is a bit of shorthand to save repeating weatherMock.demand
over and over again.
In this mock we are saying that the predictWeather
method that takes a Weather
, will be overwritten by our injected closure, and then yearlyMeanTemp
will be called and return 17.5.
The use
block runs the actual test and performs the assertions. The calls to the WeatherReport
constructor and the mocked methods are proxied within the use
block. The assertions are called, and in another good demonstration of the power of closures, an implicit verify is called at the end.
The syntax for StubFor
is almost exactly the same, except the demands in the use block will return unlimited times.
Implementing a Class Using a Map
This is my favourite Groovy feature. Its actually part of the Groovy language, but can be used for mocking. In short, Groovy lets you define an implementation of a class as a map of its method names to closures. For example:
def alwaysEqualComparable = [ compareTo: {Object o -> return 0; } ] as Comparable assert (alwaysEqualComparable instanceof Comparable)
Given that when you write a mock or stub, you are specifying an alternate implementation of an interface to use, you can use this technique for testing:
@Test void makeYourOwnImplementation() { def goodWeatherSvc = [ getTemperature : {BigDecimal lat, BigDecimal longitude -> return 25.6}, getConditions : {BigDecimal lat, BigDecimal longitude -> return "Sunny"} ] as WeatherService TravelAdvisor cut = new TravelAdvisor(); cut.setWeatherService(goodWeatherSvc); assert cut.shouldIGo(35.2, 12.123) def badWeatherSvc = [ getTemperature : {BigDecimal lat, BigDecimal longitude -> return 3.5}, getConditions : {BigDecimal lat, BigDecimal longitude -> return "Freezing"} ] as WeatherService cut.setWeatherService(badWeatherSvc) assert !cut.shouldIGo(23.21, -144.23) }
You’ll see that I’ve got a weather service interface that defines two methods, getTemperature
and getConditions
I create a map of method names to closures, where the closures are my mock implementations.
Then I say ‘as WeatherService'
, and the as
keyword generates a class instance. I can then go ahead and inject my service into the class under test.
To do the same again to test a different condition, all I need to do is define another implementation badWeatherSvc.
But there’s more…
I’ve only touched upon some of the great features of Groovy here. Some other useful features worth investigating are the Safe Navigation Operator to reduce null checks, Regular Expression shorthand, and the ability to set properties via dot notation or as constructor args.
Furthermore, like other scripting languages, Groovy makes it possible to add methods at runtime to legacy code. This is great if you don’t have the source, or you just really think the designers of your favourite API really should have added some useful method that you ended up having to write a util class for.
The Groovy devs have ‘groovified’ the entire JDK with such extensions. Here is an example of a new File.readLines()
method that reads the file and returns it back as a list of strings without the need to manage a BufferedLineReader
or InputStream:
1 |
List lines = new File(‘testInputList.txt’).readLines(); |
These enhancements are known as the Groovy JDK and worth investigating to reduce the brevity of your own tests.
Summary
As you can see, Groovy provides some useful syntax to get the more mundane aspects of Java programming and testing out of the way. Those wanting to dip their toes in a new language can try using Groovy for unit testing as a starting-point, as the risk to your production code is minimised. Given the syntax sugar provided, you’ll spend less time writing your unit tests. Furthermore, the less verbose nature of Groovy also means your tests should be more decipherable and expressive of the feature they are trying to test.
Those wanting to take more advanced features of Groovy can use closures, along with Map-based implementations and the Meta Object Protocol to re-implement objects at runtime, allowing them to be used the way you need.
Brad Rippe
Posted at 02:14h, 28 JuneKon nice article!
One little note, for “Implementing a Class using a Map”. Shouldn’t the assertion be:
assert (alwaysEqualComparable instanceof Comparable)
Isn’t alwaysEqualComparable a variable an not class definition? Thanks again for the article!
Kon Soulianidis
Posted at 09:05h, 28 JuneHi Brad,
Good pickup. This is now fixed.
Glad you liked the blog.
Kon
Pingback:The Java Specialists Symposium: Unconference + Retreat = Mad fun | The Shine Technologies Blog
Posted at 16:09h, 20 December[…] in their lightning talk toolbox without realising it, myself included – I got to present a Unit Testing with Groovy talk based on a blog I wrote earlier this […]