How to test… Java with Groovy

Java is a nice language. The JVM allows you to run the same code on multiple platforms. There is a huge standard library for many standard tasks and (Maven) repositories for all the others. It is known and used by a whole lot of software companies around the world – I would say it is the most widely used language today. I really like programming in Java. But...

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

..."Hello World" takes not one, not two... five lines?!

Let's find all strings in a list of strings that contain "t"...

List things = Arrays.asList("First", "Second", "Last");
 
List found = new ArrayList();
for (String thing : things) {
    if (thing.contains("t")) found.add(thing);
}

...seriously this is not really intuitive and it takes time, creativity and screen space to write and read it. And these are simple examples not considering custom business objects.

We came to live with these little crankinesses and found ways to work around them. But when it comes to testing they become even more annoying. Have you ever been faced with the task of testing if a list of some items returned by a method contains all items you put in before? Not as funny as it should be, right? Parsing XML or JSON is another extremely annoying thing we sometimes need to do just for testing.

Using Java for writing tests leaves us doing things we do not want to do or not testing things we do not want to leave untested.

Groovy for Testing

Duke (the Java mascot) with a Super Man cape and the Groovy logo on its chest.

"Groovy is like a super version of Java" (http://groovy.codehaus.org/)

Groovy is another JVM language that "seamlessly integrates with all existing Java classes and libraries". Indeed it integrates so well that you can code some parts of your application in Java and some in Groovy – you don't even need to put them in separate packages, just put your Java classes in /src/main/java and the Groovy classes in /src/main/groovy. You can import Java classes in Groovy classes and Groovy classes in Java and they will just work.

To be able to use Groovy for testing in your Maven project you need add groovy-all (and junit) as dependencies and configure the maven-compiler-plugin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>de.assertagile.demonstration</groupId>
    <artifactId>groovy-testing</artifactId>
    <version>1.0-SNAPSHOT</version>
 
    <licenses>
        <license>
            <name>Apache License, Version 2.0</name>
            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
        </license>
    </licenses>
 
    <developers>
        <developer>
            <id>mkutz</id>
            <name>Michael Kutz</name>
            <email>mail@assertagile.de</email>
            <timezone>+1</timezone>
            <roles>
                <role>developer</role>
                <role>tester</role>
            </roles>
        </developer>
    </developers>
 
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
 
    <dependencies>
 
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.3.9</version>
        </dependency>
 
    </dependencies>
 
    <build>
        <pluginManagement>
            <plugins>
 
                <!-- Configure the compiler plugin for Groovy -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.2</version>
                    <extensions>true</extensions>
                    <configuration>
                        <compilerId>groovy-eclipse-compiler</compilerId>
                        <verbose>false</verbose>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                        <encoding>${project.build.sourceEncoding}</encoding>
                    </configuration>
                    <dependencies>
                        <dependency>
                            <groupId>org.codehaus.groovy</groupId>
                            <artifactId>groovy-eclipse-compiler</artifactId>
                            <version>2.9.1-01</version>
                        </dependency>
                        <dependency>
                            <groupId>org.codehaus.groovy</groupId>
                            <artifactId>groovy-eclipse-batch</artifactId>
                            <version>2.3.7-01</version>
                        </dependency>
                    </dependencies>
                </plugin>
 
                <!-- Build Helper: will add Groovy sources to path -->
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>build-helper-maven-plugin</artifactId>
                    <version>1.9.1</version>
                    <executions>
                        <execution>
                            <id>add-source</id>
                            <phase>generate-sources</phase>
                            <goals>
                                <goal>add-source</goal>
                            </goals>
                            <configuration>
                                <sources>
                                    <source>${basedir}/src/main/java</source>
                                    <source>${basedir}/src/main/groovy</source>
                                </sources>
                            </configuration>
                        </execution>
                        <execution>
                            <id>add-test-source</id>
                            <phase>generate-sources</phase>
                            <goals>
                                <goal>add-test-source</goal>
                            </goals>
                            <configuration>
                                <sources>
                                    <source>${basedir}/src/test/groovy</source>
                                    <source>${basedir}/src/test/java</source>
                                </sources>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
 
            </plugins>
        </pluginManagement>
    </build>
</project>

But what is so good about Groovy for testing?

Assert

When was the last time you wrote just the keyword assert in Java? Some time ago right? Well in Groovy the same keyword does a whole lot more for you.

Let's consider this simple piece of code:

String string = "This is a String whose first half does not contain the word \"up\"";
assert string.substring(0, 32).contains("up");

In Java the result will be:

java.lang.AssertionError
	at DemoTest.someTest(DemoTest.java:15)
	(...)

So we know where to find the failing assertion. No hint why it failed and what the assertion was.

In Groovy the same code gives us:

string.substring(0, 32).contains("up")
|      |                |
|      |                false
|      This is a String whose first hal
This is a String whose first half does not contain the word "up"

	at DemoSpec.this should work(DemoSpec.groovy:8)

Here you can see the toString output of every single element after the assert. I don't know about you but this spared me a whole lot of rather annoying debugging of my test code.

Equals

Groovy generally supports operator overloading (→ Groovy User Guide on Operators). One of the most important operators for testing already is overloaded in Groovy: the equals operator "==". While in Java this operator checks if the right hand side object is identical to the one on left hand side, in Groovy the operator is overloaded with the equals method:

String objectA = "object"
String objectB = "object"
String referenceToObjectA = objectA
 
assert objectA == objectB // works in Groovy, fails in Java
 
assert objectA == objectA // works in Groovy and in Java
assert referenceToObjectA == objectA // works in Groovy and in Java

By the way, you can check for identical objects in Groovy using the keyword "is".

Strings

While 100% compatible to their Java version, Strings in Groovy have some additional features (→ Groovy User Guide on Strings).

Since a little while we can use the String Formatter to create strings with variables without the need of (rather expensive) concatenation:

int a = 4711;
String.format("The value of \"a\" is \"%1$s\".", a);

In Groovy we can add code using the "${}" syntax:

int a = 4711
assert "The value of \"a\" is \"${a}\"." == "The value of \"a\" is \"4711\"."
 
List list = ["a", "b", "c", "d"]
List invalid = ["c", "d", "e"]
assert "The given list ${list} contains the invalid elements ${invalid.intersect(list)}" ==
       "The given list [a, b, c, d] contains the invalid elements [c, d]"

Native Literals and Operators for Collections

One of my personal favourite features of the Groovy language is the built-in literals for collections (→ Groovy User Guide on Collections)

Using these makes creating lists and arrays is quite easy:

List emptyList = []
List list = ["test", "another test", "one more"]
String[] array = ["test"]

To get an item out of a list you can use get or the getAt operator:

assert list[0] == "test"
assert list.get(1) == list[1]

getAt can also handle ranges and lists of indices:

assert list[0..1] == ["test", "another test"]
assert list[2..0] == ["one more", "another test", "test"]
assert list[0,2] == ["test", "und noch einer"]

And you can also use negative indices get items relative to the end of the list:

assert list[-1] == list[list.size()-1]

For adding new elements you can use add or the leftShift operator:

list.add("new one")
assert list[-1] == "new one"
list &lt;&lt; "also new"
assert list[-1] == "also new"

By the way, strings in Groovy can be used with the same operators as lists:

assert "bar" == "foobar"[3..-1]

There is also a Map literal which looks like the one for lists but uses a colon to separate keys and values:

Map emptyMap = [:]
Map map = [a: "test", b: "another test", c: "one more"]

The getAt operator also works for Maps but expects the parameter to be a key rather than an index:

assert map["a"] == "test"
assert map.get("b") == map["b"]

Putting new entries works via put or just by assigning values via "=":

map.put("d", "new one")
assert map["d"] == "new one"
map["e"] = "also new"
assert map["e"] == "also new"

Note that the key type is implicitly String. If we want something else, then we need to use brackets around the keys:

Object objectKey = new Object()
Map map = [objectKey: "string", (objectKey): "object"]
assert map["objectKey"] != map[objectKey]
assert map["objectKey"] == "string"
assert map[objectKey] == "object"

Closures

In Groovy we can use Closures (→ Groovy User Guide on Closures) just right now – no need to wait for Java 8. Closures are put into curly braces. If there is only one parameter, it can be referenced as it, if there are more or if we want to note the parameter type explicitly, we can use "->" to separate the declaration:

Closure itContainsYes = { it.contains("yes") }
Closure stringContainsYes = { String string -&gt; string.contains("sey") }
Closure stringRangeContainsYes { String string, IntRange range -&gt; string[].contains("yes") }

Closures are used in various default Groovy methods and massively reduce the need for anonymous inner classes.

Finding elements in Collections

One of the most amazing use cases of closures for testing is finding elements in collections:

assert ["yes", "yesyes", "no", "also yes"].findAll { it.contains("yes") } == ["yes", "yesyes", "also yes"]

This, of course, works for any type of collection and also for maps:

[abc: "yes", bcd: "no", cde: "also yes"].findAll {String key, String value -&gt;;
    key.contains("b") &amp;&amp; value.contains("yes")
} == [abc: "yes"]

Just for illustration – the same in Java:

Map<String, String> map = new HashMap<>();
map.put("abc", "yes");
map.put("bcd", "no");
map.put("cde", "also yes");
 
Map<String, String> found = new HashMap<>();
for (Map.Entry<String, String> entry : map.entrySet()) {
    if (entry.getKey().contains("b") &amp;&amp; entry.getValue().contains("yes")) {
        found.put(entry.getKey(), entry.getValue());
    }
}
 
Map>String, String> expected = new HashMap<>();
expected.put("abc", "yes");
 
assert expected.equals(found);

Loops

Of course, all the loops known in Java work in Groovy exactly the same. Additionally, Groovy offers some closure based loops (→ Groovy User Guide on Looping):

["yes", "yes yes", "also yes"].each { assert it.conatains("yes") }
["a", "bb", "ccc"].eachWithIndex { String string, Integer index -&gt; assert string.length() == index + 1 }
[a: "a1", b: "b2", c: "c3"].each { String key, String value -&gt; assert key.contains(value) }

Regular Expressions

Another important tool for tests are regular expressions, and Groovy has its own operators for evaluating them (→ Groovy User Guide about Regular Expressions):

The operator "~" generates a Pattern object:

// Groovy
Pattern pattern = ~/\d{5}/
 
// Java
Pattern pattern = Pattern.compile("\\d{5}");

Note that the use of "/" simply is an alternative notation for strings but we do not need to escape backslashes.

The "==~" operator returns true of the regular expression on the right side matches the string on the left:

// Groovy
assert "53111" ==~ /\d{5}/
assert "53111" !=~ /\d{4}/
 
// Java
assert Pattern.compile("\\d{5}").matcher("53111").matches();
assert !Pattern.compile("\\d{4}").matcher("53111").matches();

The "=~" operator generates a Matcher object which allows evaluation if matching groups:

// Groovy
Matcher matcher = "53111 Bonn, 53721 Siegburg" =~ /(\d{5}) (\w+)/
assert matcher[0][0] == "53111 Bonn"
assert matcher[0][1] == "53111"
assert matcher[0][2] == "Bonn"
assert matcher[1][0] == "53721 Siegburg"
assert matcher[1][1] == "53721"
assert matcher[1][2] == "Siegburg"
 
// Java
Matcher matcher = Pattern.compile("(\\d{5}) (\\w+)").matcher("53111 Bonn, 53721 Siegburg");
assert matcher.find();
assert matcher.group(0) == "53111 Bonn";
assert matcher.group(1) == "53111";
assert matcher.group(2) == "Bonn";
assert matcher.find();
assert matcher.group(0) == "53111 Bonn";
assert matcher.group(1) == "53111";
assert matcher.group(2) == "Bonn";

Parsing XML/JSON

Dynamic typing is maybe the most obvious difference between Groovy and Java. Usually, I like strict typing since it reveals a lot of problems at compile time that would appear at run time using dynamic typing. However, it is extremely useful for instance when parsing XML (→ Groovy User Guild on parsing XML):

String xml = """
<messages>
    <message>
        <from>me@somewhere.com</from>
        <recipients>
            <to>somebody@somewhereelse.org</to>
            <cc>somebodyelse@spmewhereelse.org</cc>
        </recipients>
        <subject>This is a test message</subject>
        <body>This is a test message that is used to demonstrate the XML paring feature of Groovy.</body>
    </message>
    <message>
        <from>me@somewhere.com</from>
        <recipients>
            <to>whoever@someplace.co.uk</to>
        </recipients>
        <subject>Another test message</subject>
        <body>This is another test message that is used to demonstrate the XML paring feature of Groovy.</body>
    </message>
</messages>
"""
 
def inbox = new XmlSlurper().parseText(xml)
 
assert inbox.message.size() == 2
assert inbox.message[0].from == "me@somewhere.com"
assert inbox.message[0].recipients.recipient[0].@type == "to"
assert inbox.message[0].recipients.recipient[1].@type == "cc"
assert inbox.message.content.text().each {it.contains("test message")}

No need to define the structure of a message thanks to dynamic typing.

If you need to parse JSON, the JsonSlurper works quite the same as the XmlSlurper.

Conclusion

Groovy is an amazing language giving us much better ways of writing our tests.

If you ask yourself "Why do I use Groovy just for testing and not for production code?", ask your team members, maybe there is no good reason and you can just start by writing only new code in Groovy, keeping your good old trusty Java code.

Of course, there may be reasons to stay with Java. Dynamic typing is definitely something that may feel a little strange. There are some run time errors possible in Groovy that would be compile time errors in Java. That's why I am personally not entirely comfortable with switching to Groovy for production code.

Another one is that you can write Java like code in Groovy but also something that may not be as readable. Java is a little more structured and readable by default while in Groovy it is very easy to do very, very fancy stuff that might be cool now but may get hard for somebody else who gets to maintain your fancy code some time from now.

  • mike

    Nice post. A good intro for using groovy in addition (and comparison) to java

  • Nice writeup. Just a little pedantic remark: Your example for finding in lists from the beginning is missing the initialization of “found”, which bloats the boilerplate even more 😉

    • Thanks for the hint. Listing should be correct now.