Announcement Announcement Module
Collapse
No announcement yet.
JUnit + Spring conceptual questions (issues) Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JUnit + Spring conceptual questions (issues)

    Hiho!

    Im in progress to introduce a test environment whichour developers can use.
    We have already a project structure thtat looks like that:

    EAR Project
    http://img220.imageshack.us/img220/8060/structures.jpg


    Web Project
    Portlet/JSF specific code with the following structure

    http://img220.imageshack.us/img220/4...ucturesweb.jpg


    Services Project
    Service/Domain/DAO sources - service oriented
    http://img6.imageshack.us/img6/2310/...reservices.jpg


    Spring gets initialized through the following part in the web.xml:

    Code:
    	
           <context-param>
    		<param-name>parentContextKey</param-name>
    		<param-value>ch.XXX.XXX.sample.service</param-value>
    	</context-param>
    	<listener>
    		<listener-class>
    			org.springframework.web.context.ContextLoaderListener
    		</listener-class>
    	</listener>
    different config files are composed at service project level as the usecase is mostly deciding the wiring. The beanRefContext.xml in the service project looks like:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
    
    	<!-- Full application context -->
    	<bean id="ch.xxx.xxx.sample.service" lazy-init="false"
    		class="org.springframework.context.support.ClassPathXmlApplicationContext">
    		<constructor-arg>
    			<list>
    				<value>applicationContext-fwk-service.xml</value>
    				<value>applicationContext-fwk-dao.xml</value>
    				<value>applicationContext-fwk-util.xml</value>
    				<value>applicationContext-fwk-validation.xml</value>
    				<value>applicationContext-datasource.xml</value>
    				<value>applicationContext-service.xml</value>
    				<value>applicationContext-dao.xml</value>
    			</list>
    		</constructor-arg>
    	</bean>
    </beans>
    For our unit tests i created a very simple CommonTestCase with the following content:

    Code:
    public class CommonTestCase extends TestCase{
    	
    	
    	public ApplicationContext setUpServiceApplicationContext(){
    	   return  new ClassPathXmlApplicationContext(new String[]{"applicationContext-fwk-service.xml","applicationContext-fwk-dao.xml","applicationContext-fwk-test-util.xml","applicationContext-fwk-validation.xml","applicationContext-fwk-datasource-test.xml","applicationContext-service.xml","applicationContext-dao.xml"} );
    	}
    
    	public ApplicationContext setUpFrameworkApplicationContext(){
    	    return new ClassPathXmlApplicationContext(new String[]{"applicationContext-fwk-service.xml","applicationContext-fwk-dao.xml","applicationContext-fwk-test-util.xml","applicationContext-fwk-validation.xml","applicationContext-fwk-datasource-test.xml"} );
    	}
    	
    }
    It's a very simple way to setup the ApplicationContext for Framework and UseCase-Testing.

    My first idea was first to forget all the wiring and to do with respecting the core idea behind unit tests, by actually testing the "units" and mocking the rest. That wouldnt work out as our code mostly looks like that

    SampleService

    Code:
    public class SampleService extends CommonService implements ISampleService {
    	ISampleDAO sampleDAO;
    	
    	public void setSampleDAO(ISampleDAO sampleDAO) {
    	    this.sampleDAO = sampleDAO;
    	}
    
    
    	public Sample getSample() {
    		return sampleDAO.getSample();
    	}
    }
    So, we basically have the services delegating the job to the DAO's. It's not that our DAO's are doing business but 80% of our business logic is in the DB (yes questionable but is not most of the legacy code?).

    Forcing our developers to test such services is pointless as our services mostly dont do "anything". I introduced this structure for the only purpose to be future-proof as we're in progress of moving the business logic from DB to the service layer.

    So after forgetting the Mock approach im using different config files for our test setup, where i dont use any JNDI lookups but do simple JDBC connection or dont use the mail session from our Mail Provider in websphere etc. The config files are fetched in the CommonTestCase which is shown above in the source code.

    A typical test case looks like:

    Code:
        private ISampleService sampleService = null;
        private ApplicationContext applicationContext = null;
    
        protected void setUp() throws Exception {
    	applicationContext = setUpServiceApplicationContext();
    	sampleService = (ISampleService) applicationContext.getBean("sampleService");
        }
        
        public void testAdress() {
    	assertTrue("Sample Street".equals(sampleService.getSample().getAdress()));
    
        }

    Now i have two questions after the wall of text/pictures ;-):

    1. What do you think about the overall setup? Anything i can make in a better way?

    2. I have a weird issue:
    When i initialise the ApplicationContext with the line:

    Code:
    applicationContext = setUpServiceApplicationContext();
    in the TestCase, i get the following output in the logs:

    Code:
    21.04.2009 13:01:23 org.springframework.context.support.AbstractApplicationContext prepareRefresh
    INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3e855622: display name [org.springframework.context.support.ClassPathXmlApplicationContext@3e855622]; startup date [Tue Apr 21 13:01:23 CEST 2009]; root of context hierarchy
    21.04.2009 13:01:24 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    INFO: Loading XML bean definitions from class path resource [applicationContext-fwk-service.xml]
    21.04.2009 13:01:24 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    INFO: Loading XML bean definitions from class path resource [applicationContext-fwk-dao.xml]
    21.04.2009 13:01:24 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    INFO: Loading XML bean definitions from class path resource [applicationContext-fwk-test-util.xml]
    21.04.2009 13:01:24 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    INFO: Loading XML bean definitions from class path resource [applicationContext-fwk-validation.xml]
    21.04.2009 13:01:24 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    INFO: Loading XML bean definitions from class path resource [applicationContext-fwk-datasource-test.xml]
    21.04.2009 13:01:24 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    INFO: Loading XML bean definitions from class path resource [applicationContext-service.xml]
    21.04.2009 13:01:24 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    INFO: Loading XML bean definitions from class path resource [applicationContext-dao.xml]
    21.04.2009 13:01:24 org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
    INFO: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext@3e855622]: org.springframework.beans.factory.support.DefaultListableBeanFactory@40439621
    21.04.2009 13:01:26 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
    INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@40439621: defining beans [commonService,commonBusinessDelegate,multilanguageService,errordefinitionService,templateSqlMapFwk,applicationDataSource,templateSqlMapTplFwk,commonSqlExceptionTranslator,commonDAO,multilanguageDAO,errordefinitionDAO,mailSender,commonMailer,runtimeExceptionInterceptor,org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator#0,zipValidator,multilanguageValidator,emailAddressValidator,dataSource,sampleService,sampleBusinessDelegate,templateSqlMap,templateSqlMapTpl,sampleDAO]; root of factory hierarchy
    21.04.2009 13:01:30 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    INFO: Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
    21.04.2009 13:01:30 org.springframework.jdbc.support.SQLErrorCodesFactory <init>
    INFO: SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
    So basically it loads my "sampleService" bean. When i cast the retrieved bean to SampleService as shown with the following code fragment in the testcase class:

    Code:
    sampleService = (SampleService) applicationContext.getBean("sampleService");
    i get the following exception:

    Code:
    java.lang.ClassCastException: $Proxy3
    	at ch.xxx.xxx.sample.service.SampleServiceTest.setUp(SampleServiceTest.java:14)
    	at junit.framework.TestCase.runBare(TestCase.java:125)
    	at junit.framework.TestResult$1.protect(TestResult.java:106)
    	at junit.framework.TestResult.runProtected(TestResult.java:124)
    	at junit.framework.TestResult.run(TestResult.java:109)
    	at junit.framework.TestCase.run(TestCase.java:118)
    	at junit.framework.TestSuite.runTest(TestSuite.java:208)
    	at junit.framework.TestSuite.run(TestSuite.java:203)
    	at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
    If i cast it to the interface with :

    sampleService = (ISampleService) applicationContext.getBean("sampleService");
    it works fine... What could be the reason for this behavour
    Last edited by uenluena; Apr 21st, 2009, 06:22 AM.

  • #2
    noone with an idea or comment?

    Comment


    • #3
      Have a look at the testing chapter of the reference guide.
      You can easily select a few Spring contexts and have Spring inject your DAO in the test case. (You will need JUnit 4.4+)

      I took the following example from here
      http://static.springframework.org/sp...e/testing.html

      (Note that they forgot to use JUnit's @Test annotation on the test method)

      Code:
      @RunWith(SpringJUnit4ClassRunner.class)
      // specifies the Spring configuration to load for this test fixture
      @ContextConfiguration(locations={"daos.xml"})
      public final class HibernateTitleDaoTests {
      
          // this instance will be dependency injected by name
          @Resource
          private HibernateTitleDao titleDao;
      
          public void testLoadTitle() throws Exception {
              Title title = this.titleDao.loadTitle(new Long(10));
              assertNotNull(title);
          }
      }
      Depending on your setup, it can also be a good idea to make a Spring context that uses the in-memory database hsqldb so you tests don't need to access the database server. You can also make your test methods @Transactional.
      Last edited by SlowStrider; May 5th, 2009, 08:52 AM.

      Comment


      • #4
        Thanks for your reply but i cant use annotations in my project, as we use java 1.4

        Comment


        • #5
          Originally posted by uenluena View Post
          Thanks for your reply but i cant use annotations in my project, as we use java 1.4
          Considering that JDK 1.4 was EOl'd last fall, I'd strongly consider migrating your applications to 1.5 or 1.6.

          http://java.sun.com/j2se/1.4.2/

          We had dozens of projects running on 1.4 last year and we did just that. All of our new projects are using 1.6 with annotations and generics.

          Comment


          • #6
            Hi,

            it will be probably an issue with the spring SampleService bean definition.

            If you have a definition like this one:

            Code:
            	<bean id="sampleService" class="ch.generali.xnet.sample.service.ISampleService">
            	</bean>
            you are injecting an interface, and the bean is an Interface and not a class.

            In any case, I recommend you to take a look on the spring reference, especially the chapter that talks about testing http://static.springframework.org/sp...e/testing.html. If you have the 80% of your business code delegated into DAOs, maybe you might use the AbstractDependencyInjectionSpringContextTest or AbstractTransactionalSpringContextTest. (http://static.springframework.org/sp...ing-fixture-di).

            I hope this helps you.

            Greetings.

            Comment


            • #7
              $Proxy usually means AOP related stuff

              if you have a class called DatabaseServiceImpl
              and an interface called DatabaseService, and DatabaseServiceImpl implements DatabaseService,

              Code:
              //this works
              DatabaseService fromInterface = ctx.getBean("databaseService");
              
              //no AOP -> it works, with AOP-> it fails
              DatabaseServiceImpl fromConcrete = ctx.getBean("databaseService");
              question: why would you want to cast it back to its original class?

              Comment

              Working...
              X