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

Web Project
Portlet/JSF specific code with the following structure

Services Project
Service/Domain/DAO sources - service oriented

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