PDA

View Full Version : Integration Testing with ContextTestCase



feester
Aug 25th, 2004, 04: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&#40;"<<< CONTEXT LOADED >>> &#91;"&#41;;
for&#40; int i = 0; i < xmlFiles.length; i++ &#41; &#123;
if &#40; i > 0 &#41; &#123; msg.append&#40;" "&#41;; &#125;
msg.append&#40; xmlFiles&#91;i&#93; &#41;;
&#125;
msg.append&#40;"&#93; IN class='"&#41;;
msg.append&#40; this.getClass&#40;&#41;.getName&#40;&#41; &#41;;
msg.append&#40;"'"&#41;;
System.out.println&#40; msg.toString&#40;&#41; &#41;;
&#125;
&#125;

private boolean same&#40; String&#91;&#93; files1, String&#91;&#93; files2 &#41;
&#123;
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&#40; int i = 0; ret && i < files1.length; i++ &#41; &#123;
ret = files1&#91;i&#93;.equals&#40; files2&#91;i&#93; &#41;;
&#125;
return ret;
&#125;

private String&#91;&#93; toStringArray&#40; String listing &#41;
&#123;
String&#91;&#93; ret = null;

StringTokenizer t = new StringTokenizer&#40; listing, "," &#41;;
final int count = t.countTokens&#40;&#41;;
ret = new String&#91;count&#93;;

for&#40; int i = 0; i < count; i++ &#41; &#123;
ret&#91;i&#93; = t.nextToken&#40;&#41;.trim&#40;&#41;;
&#125;
return ret;
&#125;
&#125;

mperham
Aug 26th, 2004, 11:05 AM
You might find this interesting also:

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

rstearns01
Aug 26th, 2004, 01: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 = &#40;MyClass&#41;getBean&#40;"myClassBean"&#41;;
in each test method.

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

Regards,

Randy