views:

540

answers:

1

I asked a question, title of which might have been misleading so I'm going to try to ask the question again with much detailed stuff. (i know question seems long but please bear with me)

What I'm trying to do: I simply want to write a test case for my DAO and make it work. I know my DAO's work fine inside the container (app server) but when calling DAO from test case..it does not work. I think because its outside of the container.

Stuff in my spring-for-iBatis.xml

<bean id="IbatisDataSourceOracle" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/RSRC/my/db/oltp"/>
</bean>
<bean id="MapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
  <property name="configLocation" value="classpath:sql-map-config-oracle.xml"/>
  <property name="dataSource" ref="IbatisDataSourceOracle"/>
 </bean>

Stuff in my sql-map-config-oracle.xml

<sqlMapConfig>
   <settings enhancementEnabled="true" useStatementNamespaces="true" />
 <transactionManager type="JDBC">
  <dataSource type="JNDI">
   <property name="DataSource" value="jdbc/RSRC/my/db/oltp"/>
  </dataSource>
 </transactionManager>
         <sqlMap resource="mymapping.xml"/>
</sqlMapConfig>

my Abstract class:

public abstract MyAbstract {
    public SqlMapClientTemplate getSqlTempl() SQLException{
        public static final String ORCL = "jdbc/RSRC/PIH/eiv/oltp";
        try {
            ApplicationInitializer.getApplicationContext().getBean("MapClient");
            SqlMapClient scl = (SqlMapClient) ApplicationInitializer.getApplicationContext().getBean("MapClient");
            DataSource dsc = (DataSource) MyServiceLocator.getInstance().getDataSource(ORCL);
            return new SqlMapClientTemplate (dsc, scl);
        }
        catch (NamingException e)
        {
            log.error(ne.getMessage(), e);
            throw new SQLException("some error here: " + e.getMessage());
        }
    } 
}

my DAO:

public class MyDAO extends MyAbstract{
 public AnObject getSomething(String id)
        {
        HashMap myMap = new HashMap();
        myMap.put("id", id);
        try {
            setSqlMapClientTemplate(getSqlTempl());
        }
        catch (SQLException ne)
        {
            log.error (ne.getMessage(), ne);
        }
        getSqlMapClientTemplate().queryForList("mymapping.someproc", myMap);
        return AnObject ((List)myMap.get("firstresult").get(0));
        }
}

Mytests

public class MyDAOTests extends TestCase {

 public void testMyDAO ()
 {
  MyDAO myd = new MyDAO();
  AnObject ano = myd.getSomething("15");
  assertEquals("1500", ano.getContentId());

 }
}

I've tried to present the whole problem in this code snippet. The test fails because it is not able to get the connection to the database...since it is outside the container. I know the design can be fixed to make better use of dependency injections. Can you show me, based on this snippet, what improvements could be made so that the tests would work?

I've been struggling with this and would really appreciate some help.

PS: I had to make use of setSqlMapClientTemplate() because I want call to my DAO to be just simple MyDAO myd = new MyDAO() I do not want to make interface for each one of my DAO.

+5  A: 

There are lots of problems here.

First, I count three citations of the JNDI lookup string in your small example. DRY would tell you to write it once and refer to it, if possible.

Second, I don't appreciate your DAO very much. Is this really what you're writing, or is this just an example? I don't think this is the Spring idiom. There's no interface. How will you do declarative transactions without one? I'd recommend looking at the Spring docs for iBatis more carefully.

Third, I'd recommend using JUnit 4.4 or, better yet, the TestNG idiom - annotations. Also check out the Spring @ContextConfiguration to inject the beans you need in setUp.

Fourth, your DAOs cannot work because you need a JNDI lookup service running, and you can't get one without the container. The answer is to have a DriverManager data source for your tests.

UPDATE: Here's an idea to try: Use the Spring idiom for iBatis. If legacy prevents you from doing this, perhaps Spring isn't your answer.

Once you do this, all you have to do is override the data source app context to use DriverManager instead of JNDI for your test.

duffymo
**First**: understand that is bad but what is 1 place I should keep it? you said inject is using spring. what exactly does that mean? Of the three citings, am I using spring injection somewhere? how would i do that? **Second**: I am changing legacy code. there is lot of code that is calling the DAO methods. If I do follow the spring idiom exactly then I will have to go back and change the way DAO's are called. **Fourth**: instead of MyServiceLocator I can use DriverManager datasource but what about .getBean("MapClient");? how will I get that without the container? and thanks a lot
Drake
"I am changing legacy code" - then maybe Spring isn't the answer for your situation. What problem are you trying to solve?
duffymo
In the legacy code I am converting JDBC code to be in iBatis. If I take out the JNDI name from my Abstract class, then I wont be using MyServiceLocator. But then how Do I get the DataSource in MyAbstract class? I read chapter 11 of spring but they dont show how to get the JNDI name as a bean and have a datasource from it..
Drake
you provide a setter - setDataSource(DataSource) - and allow it to be injected by the context (xml file)
matt b
the whole point then is that your DAO is not tied to a specific datasource or JNDI string, making it possible/easy to supply different values, mock objects, etc for testing. This is the main theme of Spring - to have dependencies injected INTO a class, rather than a class going out and finding what it needs
matt b
@matt: your comment helped me finding this link: http://www.roseindia.net/spring/springpart3.shtml this looks like what I should do. Ok so this would remove hard coded JNDI string in MyAbstract class. Still one problem is, coming from the test case (outside container) how do I get MapClient bean (2nd line of try block in getSqlTempl())
Drake
Oh no, don't look at Rose India. Not a good source for anything.
duffymo