PDA

View Full Version : Integration Testing with ContextTestCase


feester
Aug 25th, 2004, 05:31 PM
About a month ago--I don't remember where--someone provided a JUnit TestCase implementation that loaded a Spring ApplicationContext then set instance variables via java reflection. I liked the concept, but have come up with a couple of refinements of my own. Maybe others will find following implemenation useful.

ContextTestCase will only load the ApplicationContext once, for a test class. If all the test classes in a test suite have same context files then ApplicationContext only loads once for the entire test suite. If the context files change from test class to test class a new ApplicationContext will be loaded/reloaded as needed.

First, create a test class that inherits from ContextTestCase.

To initialize the context call initialize method in setUp():

public void setUp() throws Exception
{
initialize( "contextFile1.xml,contextFile2.xml" );
}


To get an instance to test call getBean():

public void testMethod() throws Exception
{
final MyClass ref = (MyClass)getBean("myClassBean");
// proceed to test
}


Hope you find following implementation useful:


import java.util.StringTokenizer;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlAp plicationContext;

/**
* A JUnit TestCase extension that simplifies integration testing by making
* available a Spring application context. This class short-circuits the
* 'set all instance variables null before every test method' mechanism in favor
* of loading Spring environment only once before executing any test methods.
*/
public class ContextTestCase extends TestCase
{
private static String[] contextFiles = null;
private static ApplicationContext context = null;

protected final static Object getBean( String bean )
{
return context.getBean(bean);
}

protected final static ApplicationContext getContext()
{
return context;
}

/**
* Alternate form to initialize/load context
* @param xmlFiles in comma delimited string
*/
protected final synchronized void initialize( String xmlFiles )
throws Exception
{
String[] contextFiles = toStringArray( xmlFiles );
initialize(contextFiles);
}

/**
* @param xmlFiles context files that specify objects available
* in Spring application environment.
*/
protected final synchronized void initialize( String[] xmlFiles )
throws Exception
{
if( context == null || !same( xmlFiles, contextFiles ) ) {
contextFiles = xmlFiles;
context = new ClassPathXmlApplicationContext( xmlFiles ); // load context

StringBuffer msg = new StringBuffer("<<< CONTEXT LOADED >>> [");
for( int i = 0; i < xmlFiles.length; i++ ) {
if ( i > 0 ) { msg.append(" "); }
msg.append( xmlFiles[i] );
}
msg.append("] IN class='");
msg.append( this.getClass().getName() );
msg.append("'");
System.out.println( msg.toString() );
}
}

private boolean same( String[] files1, String[] files2 )
{
boolean ret = true;
ret = files1 != null && files2 != null && files1.length == files2.length;
// true if both not null & same length arrays

// loop stops if ret==false
for( int i = 0; ret && i < files1.length; i++ ) {
ret = files1[i].equals( files2[i] );
}
return ret;
}

private String[] toStringArray( String listing )
{
String[] ret = null;

StringTokenizer t = new StringTokenizer( listing, "," );
final int count = t.countTokens();
ret = new String[count];

for( int i = 0; i < count; i++ ) {
ret[i] = t.nextToken().trim();
}
return ret;
}
}

mperham
Aug 26th, 2004, 12:05 PM
You might find this interesting also:

http://opensource.atlassian.com/confluence/spring/display/DISC/Spring-enabled+test+case

rstearns01
Aug 26th, 2004, 02:22 PM
I've been using the SpringEnabledTestCase for a couple months, but I've relegated it to a integration-type test due to:
a) the time it takes to parse the xml file(s)
b) I dislike the dependence on the IoC container for unit tests

Normally I only have a couple collaborators to mock/stub, and I use DI rather than DL, so I just wire it by hand in setUp().

I also stash the target in an instance variable to avoid repetitions of:
final MyClass ref = (MyClass)getBean("myClassBean");
in each test method.

I like the improvement of only building the context once, though. Thanks!

Regards,

Randy