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;
}
}
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;
}
}