Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Sunday, December 19, 2004

Groovy, Junit, Eclipse

So, during my trip to Javapolis, I hit that point where I had typed one parenthesis too many. You know the one, it involves class casts, the very ones the compiler could so easily do for us automatically. Mine typically look like:

    protected IPage newPage(String name)
    {
        MockControl control = newControl(IPage.class);
        IPage result = (IPage) control.getMock();

        result.getPageName();
        control.setReturnValue(name);

        return result;
    }

That's from a bit of a unit test, where I'm using EasyMock to create unit test fixtures on the fly.

Regardless, for my unit tests, I really get sick of all the casting and types. So I'd like to start using Groovy. Recoding the above code snippet into Groovy should end up looking like:

    protected newPage(name)
    {
        control = newControl(IPage.class)
        result = control.getMock()

        result.getPageName()
        control.setReturnValue(name)

        return result
    }

No return types. No variable types. Fewer parenthesis (remaining ones are there for aesthetic reasons). Ultimately, I think this is easier to read.

Is Groovy ready for prime time? Not for me. It's very important for my development cycle to be able to hit the Run... button in Eclipse and see the clean, green sweep of the progress bar in the JUnit view.

No such luck with Groovy. I created the simplest test case I could think of:

package com.examples;

import  groovy.util.GroovyTestCase


class TestStuff extends GroovyTestCase
{

     void testSomething() {
       assertEquals (true, true)
    }
}

I then used the JUnit launch configuration to find all of my tests in my project ... which should just be this TestStuff class. Instead:

java.lang.RuntimeException: No filename given in the 'test' system property so cannot run a Groovy unit test
	at groovy.util.GroovyTestSuite.loadTestSuite(GroovyTestSuite.java:97)
	at groovy.util.GroovyTestSuite.suite(GroovyTestSuite.java:85)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:324)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.getTest(RemoteTestRunner.java:364)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:398)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:305)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:186)
Failed to invoke suite(): java.lang.RuntimeException: Could not create the test suite: java.lang.RuntimeException: No filename given in the 'test' system property so cannot run a Groovy unit test

OK. From what I can tell in the documentation, this means that Groovy has a placeholder TestCase class that is dependant on a JVM system property to identify the actual, single (!) script to execute. We'll just focus in, and run tests in my src folder instead (already less than ideal, because I often have several folders of unit test classes).

Boom. JUnit sees no unit tests in the source folder.

The only way I've found to get my Groovy unit test to execute is to use Run ... -> Groovy. That's great for running one set of tests, available in a single unit test class. HiveMind has 523 tests, Tapestry has over 580 ... these tests are spread across a large number of individual unit test classes. I don't maintain a JUnit test suite any more because JUnit support in both Eclipse and Ant will "scan" for my test cases. When I make changes, I need to easily run all of my tests.

I don't doubt I could cobble something together using Ant to get my tests to execute, but my preferred work style is to stay in Eclipse. Shutting back and forth between Eclipse and the command line, or even running Ant from inside Eclipse is not acceptible. The JUnit support inside Eclipse blows away what's available at the command line ... the direct access to code lines, the ability to get a diff on failed assertions (something you may have missed ... double click on the mismatch message in the stack trace and a diff window pops up to show you exactly what didn't match). And the general pleasantness of the green bar. I don't want to run my unit tests outside of Eclipse ... but, for the moment, Groovy is trying to force me to.

What I did eventually find was that I needed to generate a TestSuite for my Groovy tests:

package com.examples;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 * @author Howard M. Lewis Ship
 */
public class GroovySuite extends TestCase
{
    public static Test suite() throws Exception
    {
        TestSuite suite = new TestSuite("Groovy Tests");

        suite.addTestSuite(Class.forName("com.examples.TestStuff"));

        return suite;
    }
}

This works, but is not my ideal. I have to use the full qualified class name as a string, otherwise Eclipse's builder considers this an error (the Groovy builder doesn't seem to be well integrated into Eclipse, so Eclipse has no knowledge of Groovy classes). I also have to maintain this file as I add new Groovy test classes. Further, this must be coded in Java (so Eclipse knows about it). I'll probably bite the bullet, and make do with this, but it's not as nice as simply writing Groovy tests and seeing them run.

Along the way, I found out that the Groovy plugin for Eclipse is quite primitive. Syntax or other errors in the Groovy code do not display in the editor, the tasks view or the problems view. Further, the old .class file is left behind, which further muddies the water. Basically, I'm left without a lot of confidence that what I've typed into the editor is what's running ... any errors in my Groovy code and some earlier version of the code runs instead. In unit testing terms, that means the potential for a lot of false positives on tests runs!

The prevaling wisdom in the Groovy community is that the best way to get your feet wet with Groovy is to start using it for unit tests. Sounds like a great idea, but I think the Groovy team needs to focus on this use case, especially with respect to Eclipse and other IDEs.

3 comments:

James Strachan said...

From the error message "No filename given in the 'test' system property so cannot run a Groovy unit test" it looks like you tried running GroovyTestSuite in your IDE - which is a generic way of running any Groovy test case in any IDE - but by having to specify the 'test' system property to point to the groovy file to run. This clearly sucks - but its just a work around as most IDEs don't yet recognise Groovy as a native unit test case format yet.

To be able to run a Groovy unit test case as a JUnit test case inside your IDE you need IDE support. Right now the Eclipse plugin provides this (though I confess to being an IDEA guy so haven't tried it for a while). The NetBeans guys are working on native support for Groovy also. No idea when IDEA will catch up though...

Howard said...

It would work better for me if GroovyTestSuite would simply return an empty TestSuite if the test system property was null. Eclipse's JUnit plugin is doing what it promises ... finding all the implementations of TestCase and executing each.

I think the big question is how to do a proper Groovy plugin that truly lets Groovy code look like Java code to the rest of the IDE, including the JUnit plugin.

kgignatyev said...

My biggest resistance for extensive use of scripts is that my bellowed code completion does not work. It might be easy for you to write those tests, as you know your API by heart, but I would gladly tolerate extra cast because it allows code completion and syntax checks work in my IDE. And by the way IDEA does cast automatically therefore only aesthetical reason stands in favor of groovy IMO.