|
From: Bob N. <rn...@co...> - 2004-04-21 14:47:16
|
Hi Colin,
That's a good point. Currently none of my contexts have that kind of
overhead, so I've not hit this in practise (yet!).
I confess I wasn't aware of the ResourceManager extension but my concern
with sharing the app context is one of test isolation. One thing I do to
get higher test coverage is to inject mock objects within the setUp() of
my concrete test classes (in order to throw IOException's, for example).
This would break tests if I were not tearing the context down each time.
I suppose the correct way is to configure the mock objects in the
context, of course. :)
I think a useful enhancement to my class is to make it easier to specify
test-specific configuration, rather than testcase-specific. This would
then let me reuse existing application contexts for the higher levels. I
could also then remove my programmatic configuration of beans for those
cases where I test failure states.
B.
Colin Sampaleanu wrote:
> 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);
>> }
>> }
>
>
>
>
>
> -------------------------------------------------------
> This SF.Net email is sponsored by: IBM Linux Tutorials
> Free Linux tutorial presented by Daniel Robbins, President and CEO of
> GenToo technologies. Learn everything from fundamentals to system
> administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click
|