Please see this clarification of use
.
I've been experimenting with the Spring Framework since November. I've been using Spring in conjunction with JUnit in order to more easily wire up my testcases. I've not see this approach mentioned on the mailing
lists, so I thought I'd share what I have (for reference, my test application is 4000 lines of code and I have 94.1% code coverage by Clover's measure, partly because this approach makes it much easier to
factor out common tests and apply them).
Basically, all of my TestCases's extend AbstractSpringEnabledTestCase (this, in turn extends an AbstractTestCase which extends junit.framework.TestCase, so that non-spring extensions can be added).
This class, as part of setUp() and tearDown() attempts to load XML files with the same name as the testcases class and all of its parents. This allowed me to easily wire up my test fixtures. As a very useful side
effect, this also persuaded me to factor out some of the commonalities in my testcases to form a hierarchy.
Essentially, for each interface I had defined, I've ended up with an Abstract<Interface>TestCase with a corresponding Abstract<Interface>TestCase.xml with common wiring requirements. Where I have multiple implementations of a given interface it is often only necessary to create a concrete extension and simply inherit the test suite and its configuration.
A typical use of this class is to extend it, add a Spring configuration file named after the new concrete test case, and then access the context with the protected getContext() method.
I've incorporated my posted update whereby the context objects are cached.
Regards,
Robert Newson.
/*
* Created on 23-Nov-2003
*/
package com.connected.junit.spring;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* For a given TestCase, we will load the following Spring contexts;
* applicationContext.xml - the root context.
* <package>/<classname>.xml - The context for the test case.
* all parent classes of test case are also searched.
* @author rnewson
*/
public abstract class AbstractSpringEnabledTestCase extends TestCase {
private AbstractApplicationContext context;
private Log log = LogFactory.getLog(getClass());
private static Map applicationContextCache = new HashMap();
protected final ApplicationContext getContext() {
return context;
}
protected final void setUp() throws Exception {
super.setUp();
context = null;
List classHierarchy = getClassHierarchy();
for (int i = classHierarchy.size() - 1; i >= 0; i--) {
Class clazz = (Class) classHierarchy.get(i);
context = getApplicationContext(clazz,
getClassName(clazz) + ".xml", context);
}
onSetup();
}
/**
* Override this for additional setup after ApplicationContexts are
* loaded.
* @throws Exception
*/
protected void onSetup() throws Exception {
}
protected final void tearDown() throws Exception {
closeContext();
onTearDown();
}
/**
* Override this for additional teardown after ApplicationContexts are
* closed.
* @throws Exception
*/
protected void onTearDown() throws Exception {
}
protected final void closeContext() {
if (context != null) {
context.close();
context = null;
}
}
private List getClassHierarchy() {
final List result = new ArrayList();
Class superclass = getClass();
while (superclass != null) {
result.add(superclass);
superclass = superclass.getSuperclass();
}
return result;
}
private String getClassName(final Class clazz) {
final String fullClassName = clazz.getName();
return fullClassName.substring(fullClassName.lastIndexOf(".") + 1);
}
private AbstractApplicationContext getApplicationContext(final Class clazz,
final String resourceName,
final AbstractApplicationContext parentContext) {
log.debug("Attempting to locate " + resourceName);
URL url = clazz.getResource(resourceName);
if (url == null) {
return parentContext;
}
if (!applicationContextCache.containsKey(url)) {
log.info("Loading " + url);
applicationContextCache.put(url,
new ClassPathXmlApplicationContext(new String[] {url
.toString()}, parentContext));
}
return (AbstractApplicationContext) applicationContextCache.get(url);
}
}
All: Apologies for nuking some comments,they were causing a huge horizontal scroll bar making this
page unreadable.
If you want to test your service layer, do you have to rewrite everywhere (in every xml file) a global wiring ? Say for exemple you're are testing the Item service, you should have in your spring configuration file the wiring for ItemService, ItemServiceDao, HibernateSessionFactory, the TransactionManager and so on ... Wouldn't that be cumbersome to do this for every service ?
Gilles Philippart
IT Consultant