Announcement Announcement Module
Collapse
No announcement yet.
Integration Testing with ContextTestCase Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Integration Testing with ContextTestCase

    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():
    Code:
      public void setUp() throws Exception
      { 
        initialize( "contextFile1.xml,contextFile2.xml" );
      }
    To get an instance to test call getBean():
    Code:
      public void testMethod() throws Exception
      {
        final MyClass ref = (MyClass)getBean("myClassBean");
        // proceed to test
      }
    Hope you find following implementation useful:

    Code:
    import java.util.StringTokenizer;
    import junit.framework.TestCase;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * 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;

  • #2
    You might find this interesting also:

    http://opensource.atlassian.com/conf...bled+test+case

    Comment


    • #3
      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:
      Code:
      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

      Comment

      Working...
      X