|
From: Colin S. <col...@ex...> - 2004-04-21 14:20:02
|
Hi Bob,
This is a useful approach and class. My only comment is that depending
on what is set up inside the ApplicationContext, the startup time for
creating the appcontext is long enough (even 5 seconds is pretty long)
that it may be overkill to set up the context for every single test,
especially given the fact that the context is relatively static and
contains singletons or produces new beans as prototypes. In that
respect, sharing an application context instance for all tests within a
TestCase can end up saving a lot of time across a whole project full of
TestCases. When you get into more 'integration' type tests that do
something like start up a Hibernate SessionFactory, it _really_ saves a
lot of time.
What I do to share an appcontext across a whole TestCase, is use some
helper classes from the junit extensions package, as follows:
...
// --- attributes
// per-TestSuite specific vars, via ResourceManager!
ApplicationContext appContext;
...
public static Test suite() {
return new TestSetup(new TestSuite(BasicMappedPersistenceTest.class)) {
protected void setUp() throws Exception {
try {
ApplicationContext appContext;
appContext = new ClassPathXmlApplicationContext(new String[]
{CONTEXT, MAPPER_CONTEXT});
ResourceManager.addResource(APP_CONTEXT_ID, appContext);
}
catch (RuntimeException t) {
// just for debugging since Eclipse swallows these
throw t;
}
catch (Error e) {
// just for debugging since Eclipse swallows these
throw e;
}
}
protected void tearDown() throws Exception {
ResourceManager.removeResource(APP_CONTEXT_ID);
}
};
}
protected void setUp() throws Exception {
appContext = (ApplicationContext)
ResourceManager.getResource(APP_CONTEXT_ID);
}
protected void tearDown() throws Exception {
appContext = null;
}
Essentially the junit extensions ResourceManager class allows you to
share certain object instances across the whole TestCase. This approach
would of course also probably work when assembling a test suite out of a
whole bunch of testcases.
Regards,
Colin
Bob Newson wrote:
> Hi,
>
> 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.
>
> I'm interesting if anyone thinks this is a useful approach (or is it
> too obvious?). I'm also interested if anyone thinks it's a bad idea!
>
> I've appended the full source of AbstractSpringEnabledTestCase below.
> If anyone wants a more complete example, I could spend some time on that.
>
> 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.
>
> Regards,
> Robert Newson.
>
> /*
> * Created on 23-Nov-2003
> */
> package com.connected.junit.spring;
>
> import java.io.IOException;
> import java.net.URL;
> import java.util.ArrayList;
> import java.util.List;
>
> import org.springframework.context.support.AbstractApplicationContext;
> import
> org.springframework.context.support.ClassPathXmlApplicationContext;
>
> import com.connected.junit.AbstractTestCase;
>
> /**
> * 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
> AbstractTestCase {
>
> private AbstractApplicationContext context;
>
> protected ApplicationContext getContext() {
> return context;
> }
>
> protected 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);
> }
> }
>
> protected void tearDown() throws Exception {
> if (context != null) {
> context.close();
> }
> }
>
> private List getClassHierarchy() {
> List result = new ArrayList();
> Class superclass = getClass();
> do {
> result.add(superclass);
> } while ((superclass = superclass.getSuperclass()) != null);
> return result;
> }
>
> private String getClassName(Class clazz) {
> String fullClassName = clazz.getName();
> String result =
> fullClassName.substring(fullClassName.lastIndexOf(".") + 1);
> return result;
> }
>
> private AbstractApplicationContext getApplicationContext(
> Class clazz,
> String resourceName,
> AbstractApplicationContext parentContext)
> throws IOException {
> URL url = clazz.getResource(resourceName);
>
> if (url == null) {
> return parentContext;
> }
>
> return new ClassPathXmlApplicationContext(
> new String[] { url.toString()},
> parentContext);
> }
> }
|