Apache Groovy: Assertions for clearer, more reliable code
In previous articles, I went over some basics of Groovy. If you want to unlock the power of Groovy, this series will guide you through what makes it such a valuable language for developers. (If you haven’t installed Groovy yet, please read the intro to this series.)
As you’ve seen, Groovy is (by default) a dynamically typed language. If you’re not sure what this means, here’s the breakdown:
var m = new Map<String,Integer>()
In Java results in the compiler determining the type of m at compile time by inferring it from the right-hand side. Groovy (by default) type determination, checking, and coercion takes place at runtime — thus it is “dynamically” typed as opposed to “statically” typed in Java.
Having refreshed your memory, let’s summarize the three main takeaways:
- Groovy relishes its dynamic typing by design. It provides many interesting mechanisms to investigate, such as meta-programming and expandable metaclasses. These offer many of the wide-open typing flexibility such as that offered by Python, but within a formal and controlled scope.
- Groovy code, whether entire classes or only certain methods within, can be compiled as statically typed and with strong type checking.
- The concept of “good code” in a dynamically typed programming language is inextricably linked to the concept of testing and maybe even test-driven development.
If you’re interested in using static typing, review this official Groovy documentation. To continue exploring Groovy’s metaprogramming capabilities, take a look at this official Groovy documentation. Thanks to the Grails web application framework, I have a lot of hands-on experience with other people’s metaprogramming. The design is impressive, but highly metaprogrammed classes make debugging errors a nightmare. The tracebacks can be hundreds of lines long and refer to many classes that I’ve never heard of before.
This brings me back to the importance of testing and the key feature Groovy provides to help with testing — the powerful assert statement.
Groovy users have most likely encountered the wonderful compact examples of Groovy code on Mr. Haki’s blog. It uses the Groovy assert statement to demonstrate the expected result of some code construct.
Java users are already familiar with its assert statement that was added back in version 1.4. In comparison to the Java version, the Groovy version is more informative and it’s never disabled by default. Let’s take a closer look:
1 def l1 = [1, 2, 3, 4]
2 def l2 = [*(1..4)]
3 println "l1 $l1"
4 println "l2 $l2"
5 println "l1 == l2 ${l1 == l2}"
6 println "l1 === l2 ${l1 === l2}"
7 assert l1 == l2
8 assert l1 === l2
Line one defines a new List instance, l1, whose elements are the integers between one and four.
Line two defines a new List instance, l2, using the spread operator – the * – applied to the range 1..4 to create a list containing the integers between one and four.
Lines three to four print out l1 and l2.
Lists referred to by l1 and l2 are functionally equal, but l1 and l2 are unlikely to refer to the same identical list. You may recall from post six on parameters in this series that == tests functional equality and === tests “identicality”.
Line five prints out true if l1 is functionally equal to l2.
Line six prints out true if l1 is identical to l2.
Lines seven to eight use the Groovy assert statement to test these two conditions. An assert that fails will throw an instance of java.lang.AssertionError. An assert that succeeds is silent about it.
So you can say that five and six more or less solve the same problem as seven and eight, albeit confirming positively when the conditions are true, not just when they’re false.
When you run this script, you’ll see:
$ groovy Groovy18a.groovy
l1 [1, 2, 3, 4]
l2 [1, 2, 3, 4]
l1 == l2 true
l1 === l2 false
Caught: Assertion failed:
assert l1 === l2
| | |
| | [1, 2, 3, 4]
| false
[1, 2, 3, 4]
Assertion failed:
assert l1 === l2
| | |
| | [1, 2, 3, 4]
| false
[1, 2, 3, 4]
CODE
at
Groovy18a.run(Groovy18a.groovy:10)
Before running this, you might have reasoned that, l1 == l2 is true and l1 === l2 is false. But look at the info provided by the assert statement when the expression is false you see:
- The values of the expressions in the assert statement.
- Where the false result occurred that triggered the assert statement is.
Now imagine inserting informative insert statements like this in your code while developing and testing and leaving them there to provide better maintainability. It’s fair to claim that assert statements leave no reason for the programmer to make assumptions when you can test them so easily.
Now that I’ve got you at least a little bit interested in the topic of testing, you might want to take a deeper dive with the official Groovy documentation.
Conclusion
The assert statement is a powerful tool for verifying code behavior directly within your code. Its concise syntax and informative error messages make it ideal for catching issues early in the development process. For more robust and maintainable Groovy code, incorporate assertions throughout your projects.
