The Holy Java

Building the right thing, building it right, fast

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

Posted by Jakub Holý on October 18, 2011

If this post irritates you, here's st. positive to concentrate on

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);

(Of course this starts to pay of if either you don’t want to create a constructor or if there are “many” properties and you need to set different subsets of them (constructor with 4+ arguments is hard to read).)

Reading a file:

assert test.method() == new File("expected.txt").getText()
// Java: buffered reader line by line ...; Note: == actually uses 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"]
  • Or more fail-safe regexp:
    assert ("dog1-and-dog2" =~ /dog\d/).iterator().toSet() == ["dog1", "dog2"].toSet()
    
  • With a match group:
    assert ("dog11-and-dog22" =~ /dog(\d+)/).iterator().collect({it[1]}).toSet() == ["11", "22"].toSet()
    

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!

Addendum

I don’t meen to say that you should start writing all your tests in Groovy (or Scala, JRuby, ..) right now and that whoever writes them in Java is a #!&$ (insert a pejorative adjective of your choosing). If you are perfectly happy with Java, that’s fine. If you don’t like Groovy (Scala/JRuby/…) or have a negative experience with it, that’s fine too. But if you feel after reading this that using Groovy/… might make you a more productive and happy developer then I humbly recommend that you try it and evaluate for yourself whether it is a positive change or not.

And yes, Kent Beck and Chuck Norris do write their tests in Java.

I apologize to all who feel irritated or offended by this post. That was not intended. Please eradicate it from your memory.

Warning

Unfortunately I have to say that despite all the benefits of Groovy I wouldn’t recommand using it in Eclipse (3.7). The support there is very week, for example basic refactorings like creating a method/class used in the test but not really existing isn’t supported. Contrary to that, IntelliJ is perfectly suitable for using Groovy. I haven’t tried NetBeans.

Additional Links

About these ads

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

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

  2. ignorante said

    Interesting topic. I guess not having the test compiled forces you to run them in order to fix compile time errors when the code you are testing changes.

    Does the refactor code feature (rename method, rename class, etc.) of the IDE work on Groovy code?

    • @Ignorante: Regarding compilation, the tests are actually compiled even though it is a scripting language (I guess most IDEs support that). And the point here actually isn’t the “scripting” side of Groovy but its enormous productivity.

      Regarding refactoring: Yes, it works on Groovy and across Java and Groovy, at least in IntelliJ 10.5.

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

  4. joe said

    Scala has same java interoperability like Groovy. It now also has the specs2 library which to me seems like a typesafe alternative to spoc. It has data tables with html output etc…

    http://etorreborre.github.com/specs2/guide/org.specs2.guide.Matchers.html#DataTables

    • Thank you, Joe! I believe that Scala is also a great language for this – you loose the copy & paste into Java potential of Groovy but you gain type safety and perhaps other benefits. And it’s certainly a good way to start experimenting with Scala.

  5. merryme said

    only a masochist would not use spock, the phenomenal groovy testing framework @ spockframework.org

  6. JJ said

    Bean construction:
    new Customer(“Bob”, “Newt”, 42);

    Reading a file:
    assertEquals(Resources.toString(Resources.getResource(“expected.txt”), Charsets.UTF_8), test.method());

    Checking the content of a collection/map:
    assertEquals(sort(Collections2.transform(customers, Customer.SURNAME_FUN)), Arrays.asList(“Lizard”,”Newt”));

    assertEquals(getCapitalsMap(), ImmutableMap.of(“UK”, “London”, “CR”, “Prague”));

    Regular expressions: Bad practice, regexes are just too ugly and hard to read

    I didn’t try to compile these but you get the idea.

  7. Reblogged this on Zeeshan Akhter.

  8. Tomas said

    Have you ever tried this with Eclipse? I tried to write tests in one project in Groovy (with Eclipse as IDE), but found a big problem with the fact that Eclipse doesn’t propagate Java code rafactoring to the groovy tests, which are testing that code (i.e. method name changes). I am not sure if this is just me not having Eclipse configured correctly, or if support for Groovy is simply that bad in Eclipse.

    • Hi Tomas, yes, support for Groovy in Eclipse is poor, too poor to make Groovy tests worth, I’m afraid. I can only recommend to use them with IntelliJ (I haven’t tried NetBeans thugh).

Sorry, the comment form is closed at this time.

 
%d bloggers like this: