Platinum Partner
java,testing

Only a Masochist Would Write Unit Tests in Java. Be Smarter, Use Groovy (or Scala…).

I like writing unit tests but Java doesn’t make it particularly easy. Especially if you need to create objects and object trees, transform objects for checking them etc. I miss a lot a conscise, powerful syntax, literals for regular expressions and collections, conscise, clojure-based methods for filtering and transforming collections, asserts providing more visibility into why they failed. But hey, who said I have to write tests in the same language as the production code?! I can use Groovy – with its syntax being ~ 100% Java + like thousand % more, optional usage of static/dynamic typing, closures, hundreds of utility methods added to the standard JDK classes and so on. Groovy support for example in IntelliJ IDEA (autocompletion, refactoring …) is very good so by using it you loose nothing and gain incredibly much. So I’ve decided that from now on I’ll only use Groovy for unit tests. And so far my experience with it was overwhelmingly positive (though few things are little more complicated by the positives more than compensate for them). Read on to find out why you should try it too.

(The arguments here focus on Groovy but I guess similar things could be said about JRuby, Scala etc. – with the exception of Java code compatibility, which you only get in Groovy.)

Few examples

Some of the example below use some Groovy magic but don’t be scared. You can write Groovy just as if it was Java and only learn and introduce its magic step by step as you need it.

Bean construction:

def testBean = new Customer(fname: "Bob", sname: "Newt", age: 42)
// Java: c = new Customer(); c.setFname("Bob"); c.setSname("Newt"); c.setAge(42);

Reading a file:

assert test.method() == new File("expected.txt").getText()
// == actually calls equals

Checking the content of a collection/map:

assert customerFinder.findAll().collect {it.sname}.sort() == ["Lizard","Newt"]
// Java: too long to show here (extract only surnames, sort them, compare ...)
assert getCapitalsMap() == ["UK" : "London", "CR" : "Prague"]

Regular expressions:

assert ("dog1-and-dog2" =~ /dog\d/).getAt([0,1]) == ["dog1", "dog2"]

What is Groovy?

Groovy 1.8 is a mature scripting language for the JVM written as an extension of Java with optional dynamic typing, closures, and more. It’s now developed by SpringSource, the company behind the Spring framework.

As mentioned, nearly all Java code is a valid Groovy code with basically one exception: To initialize an array you can’t use { … } (for that would be a closure), you must use [...] instead (notice that by default it actually creates a List, only when assigned to an array variable or casted to array will it produce an array). Make sure to check the few common gotchas before you dive into using Groovy.

You can experiment with Groovy online in the GAE Groovy console.

Groovy features especially beneficial for testing

General

  • Dynamic typing at request => conscise, avoids code cluttered with casts

Collections: Closure-based methods like every, each, find

Files: Read all the text with one call, withReader { .. } etc.

Testing/advanced:

  • assert- you can use the Java keyword assert instead of JUnit methods, upon failure Groovy will provide you with pretty good info of what went wrong:
    Assertion failed: 
    
    assert config.getResolvers()) == ["h:dataTable" : resolver, "my:dt2" : null]
           |      |                |                  |
           |      |              false                |
           |      |                                   MyResolver@731d2572
           |      [h:dataTable:MyResolver@731d2572, my:dt2:MyResolver@731d2572]
           LocalVariableConfiguration@7e859a68
  • Here-docs: embed multi-line strings easily in a test (also supports replacing references like $variable with values)
  • Implementing interfaces with a map (map coercion)
  • Use expandos to define dynamic beans (similarly to JavaScript, you instantiate an Expando and just add properties and closures as methods) – as described on the linked page, expandos and maps are usually enough to replace a mocking library
  • Build-in mocking
  • With Groovy you can of course also use Spock, the excellent specification & testing framework

Complications

  • Neither JUnitMax nor Infinitest (in IntelliJ) seem to support Groovy test cases
  • You need a decent IDE such as IntelliJ IDEA
  • If using Maven, you have to explicitely configure the GMaven plugin (esp. with a newer Groovy version)
  • IntelliJ 10.5: Click & alt+enter on a non-existing method to create it only works if the target type is a nested class within the test, not if it is a standalone Java class (so I just create my class there and when done TDDing I extract it into a top-level Java class)

Conclusion

Groovy makes test writing much more productive and thus developers happy. I intend to use on all my open source projects and to try push it into our commercial projects too. You should give it a try too!

Additional Links

 

From http://theholyjava.wordpress.com/2011/10/18/only-a-masochist-would-write-unit-tests-in-java-be-smarter-use-groovy-or-jruby-or-st-else-similar/

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}