The Groovy language allows testing with inline code, it provides the built-in JUnit, Mock, stubs, and other support. Presented here are two simple ways of embedding a JUnit test class inside a Groovy class.
In a prior post, “List sorting using topological ordering of a digraph“, I used this embedded test approach in the Java coding. But this embedding did not work when I tried it with a Groovy script I was working on. I revisited this technique to see how to do with Groovy.
Note that I am interested in directly running a class or script and having the embedded JUnit test run. One could of course just invoke the embedded test class using a JUnit runner. If it is possible to run the nested class, why have the class or script runnable as a test? Good question.
GroovyEmbeddedTests\src>java -Dtest.script=true -cp .;..\bin;..\lib\junit-3.8.1.jar;\java\groovy\embeddable\groovy-all-2.1.2.jar;\java\groovy\lib\asm-4.0.jar junit.textui.TestRunner com.octodecillion.testing.Example3
Nested Tests?
Should JUnit type test classes be embedded within the class or script system under test (SUT)? About the only clear advantage is that instead of two files you have one.
In terms of deployment, Java one has the option of shipping only the non-test compiled classes. If one is shipping ‘script’ source itself, that is much harder to do.
There are some ‘human’ advantages to embedded tests. For one they are in one’s face, not another file that the average developer won’t even look at. See Ben Christensen’s blog post for further discussion.
Approach
Groovy classes
To run nested test classes we use the approach that Groovy uses in its GroovyShell class. This class is pretty neat; the runScriptOrMainOrTestOrRunnable method can run a Class, Runnable, Script, JUnit3, JUnit4, and even wash dishes. The private method that runs a JUnit test is what I needed, so I copied it into a new EmbeddedTestUtil class, shown in listing 4.
Groovy Scripts
While running nested tests is easy in Groovy classes, Scripts are very different. The Groovy compiler compiles the Groovy script into a Java class where all the ‘script’ code is put into the run() method, and a main entry point is added to invoke that run method. There are other issues of course, such as bindings, field declarations, and so forth.
One way of running nested tests in a script is to signal this desire with a system property. If that property is set, the test class is run via the appropriate JUnit runner. These tests can themselves run the target script by invoking the run method.
Examples
In listing 1, the Example1 class contains a nested ExampleTest1 JUnit 3 test class. To run this test class, we add a ‘main’ entry point to the top level class. This entry point will instantiate the test class and then use JUnit’s test runner to invoke it’s implicit ‘run’ method.
package com.octodecillion.testing import junit.framework.TestCase; import junit.textui.TestRunner import org.codehaus.groovy.runtime.InvokerHelper; /** */ class Example1 { String name; /** Nested JUnit3 test class */ static class ExampleTest1 extends GroovyTestCase{ private Example1 ex protected void setUp(){ ex = new Example1() ex.name = "hello" } public void test1(){ println "in test1 ...." assert(ex.name.equals("hello")) } } /** * Run the {@link Example$ExampleTest} tests. * */ static main(args) { EmbeddedTestUtil.runJUnit3Test(Example1.ExampleTest1) } }
An alternative to a nested test class, we can simply put the test class within the same class source file as in listing 2.
package com.octodecillion.testing import junit.framework.TestCase; import junit.textui.TestRunner import org.codehaus.groovy.runtime.InvokerHelper; /** */ class Example2 { String name; /** Run {@link ExampleTest2} tests. */ static main(args) { EmbeddedTestUtil.runJUnit3Test(ExampleTest2) } } /** Inline JUnit3 test class */ class ExampleTest2 extends GroovyTestCase{ private Example2 ex protected void setUp(){ ex = new Example2() ex.name = "hello" } public void test1(){ println "in test1 ...." assert(ex.name.equals("hello")) } }
In listing 3 we attempt to test a Groovy script.
/** */ package com.octodecillion.testing import junit.framework.TestCase; import junit.textui.TestRunner import org.codehaus.groovy.runtime.InvokerHelper; import java.util.concurrent.* if(System.getProperty("test.script")){ Executors.newSingleThreadExecutor().execute(new Runnable(){ void run() { EmbeddedTestUtil.runJUnit3Test(ExampleTest3) } }) return; } String name; println "Hello world!" /** Nested JUnit3 test class */ class ExampleTest3 extends GroovyTestCase{ def Example3 ex protected void setUp(){ System.clearProperty("test.script") ex = new Example3() ex.name = "hello" } public void test1(){ println "in test1 ...." assert(ex.name.equals("hello")) } /** Run the script */ public void test2(){ println "in test2 ...." ByteArrayOutputStream baos = new ByteArrayOutputStream() def myOut = System.out def pout = new PrintStream(baos) System.setOut(pout) ex.run() System.setOut(myOut) pout.flush(); def actual = baos.toString() assert(actual.contains("Hello world!")) } }
We copied some of the JUnit support built into Groovy into a new utility class shown in listing 4.
package com.octodecillion.testing import junit.framework.TestCase; import junit.textui.TestRunner import org.codehaus.groovy.runtime.InvokerHelper; /** * Invoke embedded test classes. * * Based on code from groovy.lang.GroovyShell. * See http://groovy.codehaus.org/gapi/groovy/lang/GroovyShell.html * @author j. betancourt */ class EmbeddedTestUtil { /** * Run the JUnit 3 test. * * Copied the approach used in * groovy.lang.GroovyShell.runJUnit3Test(Class scriptClass) * @see https://github.com/groovy/groovy-core/blob/master/src/main/groovy/lang/GroovyShell.java */ static runJUnit3Test(Class clazz) { try { Object testSuite = InvokerHelper. invokeConstructorOf("junit.framework.TestSuite", clazz); InvokerHelper.invokeStaticMethod( "junit.textui.TestRunner", "run", testSuite); } catch (ClassNotFoundException e) { throw new GroovyRuntimeException( "Failed to run the unit test. JUnit is not on the Classpath.", e); } } /** * Run the JUnit 4 test. * * Copied the approach used in * groovy.lang.GroovyShell.runJUnit4Test(Class scriptClass) * @see https://github.com/groovy/groovy-core/blob/master/src/main/groovy/lang/GroovyShell.java */ static runJUnit4Test(Class clazz, def loader) { def junit4Utils = "org.codehaus.groovy.vmplugin.v5.JUnit4Utils" try { return InvokerHelper.invokeStaticMethod(junit4Utils,"realRunJUnit4Test", [clazz, loader]); } catch (ClassNotFoundException e) { throw new GroovyRuntimeException( "Failed to run the unit test. JUnit is not on the Classpath.", e); } } private EmbeddedTestUtil(){ // } }
In listing 5 below, I run the examples in a Windows shell.
src>groovy -cp . com\octodecillion\testing\Example1 .in test1 .... Time: 0.023 OK (1 test) src>groovy -cp . com\octodecillion\testing\Example2 .in test1 .... Time: 0.018 OK (1 test) src>groovy -cp . -Dtest.script=true com\octodecillion\testing\Example3 .in test1 .... .in test2 .... Time: 0.047 OK (2 tests) </div>
Conclusion
Presented was a possible alternative to Groovy test source code structuring using nested test classes. Classes and scripts were given as examples. Perhaps a more powerful approach is to use more high-level test frameworks, such as Behavior Driven Development (with for example, JBehave). This would allow the target class or script to also contain its own executable specification.
Environment
Eclipse Kepler (4.3)
Groovy 2.1.2
Java JDK 1.7.0_25
Windows 7 64bit
Further Reading
- Nested Test Case Classes
- JUnit Tests as Inner Classes
- When To Use Nested Classes, Local Classes, Anonymous Classes, and Lambda Expressions
- Built-in-self-tests?
![Creative Commons License](http://creativecommons.org/images/public/somerights20.png)