Quantcast
Channel: T. C. Mits 108 » testing
Viewing all articles
Browse latest Browse all 18

Testing Groovy with Nested Test Classes

$
0
0

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.

Running nested test directly:
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
 
I was reading a JavaScript programming book the other day. The author talked about statistics that show most JavaScript is untested, i.e., has no tests. I bet that extends to other uses of other scripting languages. The only test is to use them and see if they still do what they should. (BTW, for testing JavaScript, Jasmine is a nice framework.)

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.

Listing 1, Groovy class with nested test
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.

Listing 2, Groovy class source file with inline test
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.

Listing 3, Groovy script with nested test
/**  */
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.

Listing 4, EmbeddedTestUtil class
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.

Listing 5, run of tests in 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

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Viewing all articles
Browse latest Browse all 18

Trending Articles