Jakub is a Java EE developer since 2005 and occasionally a project manager, working currently with Iterate AS. He's highly interested in developer productivity (and tools like Maven and AOP/AspectJ), web frameworks, Java portals, testing and performance and works a lot with IBM technologies. A native to Czech Republic, he lives now in Oslo, Norway. Jakub is a DZone MVB and is not an employee of DZone and has posted 155 posts at DZone. You can read more from them at their website. View Full User Profile

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

10.20.2011
| 7544 views |
  • submit to reddit

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/

Published at DZone with permission of Jakub Holý, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Mladen Girazovski replied on Fri, 2011/10/21 - 1:35am

I disagree, no offense intended.

IMHO only a masochist would use Groovy for isolated unit testing of Java Code, with no support for refactoring (at least in Eclipse), so each change in the prod code would need a manual change in the test code.

Also, only a masochist would directly reference a constructor in unit tests, because if the constructor is changed, all the test that used it will fail (not compile), use a factory for this purpose, Martin Fowler calls it "ObjectMother" (http://martinfowler.com/bliki/ObjectMother.html).

All in all i have no problems with writing  isolated unit tests in Java, it's very fast actually and there is good mocking frameworks around, my favourite is JMock2.

If the standard assert of JUnit are not verbose enough, the built in assertions are to verbose or you find yourself writing the same assertions over and over again, use custom asserts: http://xunitpatterns.com/Custom%20Assertion.html

CustomAsserts will also address the problems you mentioned in most of your examples (comparing collections, etc.).

Groovy can be very useful for functional testing tough, since black box tests are fragile by nature and refactoring isn't nessecary (black box, right?).

 

Piotr Kochanski replied on Fri, 2011/10/21 - 1:44am

@Mladen is 100% right. BTW only masochists are using JUnit, not TestNG.

Erwin Mueller replied on Fri, 2011/10/21 - 2:23am

@Mladen

Just use a good IDE, like Eclipse. I didn't had any problems with tests and Groovy, even with refactoring. It's either you get a bunch of libraries for your tests,or you use one (groovy-jar) and have also a better syntax.

What the author didn't mentioned, was:

use Strings as method names:

@Test
void "Your test that tests something"() { }

easy exception handling
properties (works with Java):

void getProperty() { }
obj.property == something

It is of big advantage to have your test code be little and precise as possible. Then it's easy to understand what the test is doing.

Mladen Girazovski replied on Fri, 2011/10/21 - 2:52am

Erwin,

you sure that i can for instance refactor method names, method signatures, interfaces etc. pp. in the Java prod code and those changes will be applied by Eclipse to the groovy test code? Last time i checked (was last year though) support for Groovy in Eclipse was rather basic, and no automated refactoring of the groovy code if the Java Code was changed, but maybe i have missed something?

I don't understand what you mean by "easy exception handling" in test code, if an unexpected exception occurs, the test has failed, end of story imho, it means that all i need in my test methods is the "throws exception", doesn't get much easier than that.

Of course i agree that test code should be as short as possible, ie. only showing things that are important to the test, factories/builders, utility methods, helpers and custom asserts do the job just fine imho, plus i get all the IDE and tooling support for Java, and there is still much more for Java than there is for Groovy.

Apart from that i prefer to TDD, and writing an isolated unit test in groovy just to be implementing the code under test in java a couple of seconds later doesn't seem to be easier to me than writing everything in one language.

I can get very religios about seperating (isolated) unit tests from integration tests and functional tests, if you have a high test coverage, you'll have hundreds (or even thousands) of testcases, you want the code to be organized and (very) clean, or else the tests will slow down development eventually.

Jakub Holý replied on Fri, 2011/10/21 - 5:45am in response to: Mladen Girazovski

@Mladen You might be right about Eclipse but f.ex. IntelliJ has an excellent support for Groovy including refactoring.

What I miss most in Java - and have in Grooy - are literals for lists, maps, and RegExps and powerful & easy collection transformation. Just imagine the code you'd need to translate my examples into Java.

Apart from that i prefer to TDD, and writing an isolated unit test in groovy just to be implementing the code under test in java a couple of seconds later doesn't seem to be easier to me than writing everything in one language.

I respect your opinion and approach. For me it works great with TDD to start implementing the class inside the test as a nested static class (Java is Groovy so I just limit myself to the Java syntax inside the class) so that I even needn't to switch editors; when it grows big enough I just move it into a standalone java file. Groovy and Java can be so similar that I don't see a problem in switching between them - no more than on a recent project where the production code required Java 1.4 while tests used 6.0.

Thank you for your feedback!

Thomas Eichberger replied on Fri, 2011/10/21 - 3:01pm

I agree with Jakub. I started to use Groovy for my tests a long while ago and still love it.

Ed Staub replied on Fri, 2011/10/21 - 3:43pm

Only a masochist would call everyone who doesn't agree with them a masochist.

Michael Campbell replied on Sat, 2011/10/22 - 9:06am

> with no support for refactoring (at least in Eclipse),

 

If you're relying on a tool for your language to be made usable, you're doing it wrong.

Jakub Holý replied on Sun, 2011/10/23 - 4:30pm in response to: Ed Staub

Hi Ed, don't take it personally. It actually wasn't meant as a classification of those who have other opinions - it was a feeling I had when I started using Groovy for tests - the though "How could I have been so masochist and use Java while I could have been using Groovy?!". Of course other people may have other concerns, experiences and forces around them that make this particular choice suboptimal for them. That's OK, I wanted to inspire just those who have the potential of benefiting from this as I did.

@Thomas Thank you for sharing your experience!

Jonathan Fisher replied on Mon, 2011/10/24 - 12:47pm

Only an idiot would write tests in an unstable emergent language.

Jeff Bowman replied on Mon, 2011/10/24 - 1:32pm

Probably also matters what framework you are using. I was very excited to try spock, it lays out tests much like user stories and acceptance criteria therein. I was particularly disappointed by the fairly simplistic approach to mocking which wouldn't allow me to mock a call to a method and verify the parameters the way I could with other tools. We ended up going back to Java with Mockito and JMockit for out test framework(s). I'm fairly quick with it, but I do like the spock dsl for tests. It just isn't as useful to me right now. As an aside, junit's Assert with static imports can be made as simple as: assertThat(actual, is(expected)); // org.hamcrest.CoreMatchers.is in this case very readable in my opinion. :-)

Cosmin Mutu replied on Tue, 2011/10/25 - 6:17am

def testBean = new Customer(fname: "Bob", sname: "Newt", age: 42)
// Java: c = new Customer(); c.setFname("Bob"); c.setSname("Newt"); c.setAge(42);
-----------------------------------
Ok, so why not : c = new Customer("Bob", "Newt", 42); 
Oh, I know why .. because you`re advertising Groovy .. pls, learn more java before using Groovy. 

Jakub Holý replied on Tue, 2011/10/25 - 8:06am in response to: Cosmin Mutu

Hi Cosmin, I believe that my 5 years of enteprise Java development gave me some knowledge of the language :-)

You can of course create a consturctor for that but

1) Do you really want to create a constructor in the prod. code only for the sake of the tests?

2) What if different tests need diff. subset of the properties?

3) What if you have 10 properties to set? (I know, that's bad design but we professional Java devs got to work with legacy code pretty enough.)

But don't feel offended and use Java if it makes you happier.

Cosmin Mutu replied on Wed, 2011/10/26 - 2:30am in response to: Jakub Holý

:) I didn`t felt offended ... it`s just that I didn`t liked the example you gave (the 1st one).

Other examples were ok, those options are not available in Java (well, I hardly believe that there is no library out there doing the same stuff, but I`ll give you that, they don`t deliver with the standard java).

Also the title is evil, calling ppl names :) ... masochists and stupids (be smarter, use that and that) :) ... try picking your titles better, when you write for public audience

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.