как провести интеграционное тестирование DAO, созданного с помощью Spring + iBatis

Я задал вопрос, название которого могло ввести в заблуждение, поэтому я собираюсь попробуйте задать вопрос еще раз с более подробным описанием. (я знаю, что вопрос кажется длинным, но, пожалуйста, потерпите меня)

Что я пытаюсь сделать: я просто хочу написать тестовый пример для своего DAO и заставить его работать. Я знаю, что мой DAO отлично работает внутри контейнера (сервера приложений), но при вызове DAO из тестового примера ... он не работает. Я думаю, потому что это вне контейнера.

Заполните мой файл 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>

Материал в моем 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>

мой абстрактный класс:

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());
        }
    } 
}

мой 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());

 }
}

Я попытался представить всю проблему в этом фрагменте кода. Тест не пройден, потому что он не может установить соединение с базой данных ... поскольку она находится за пределами контейнера. Я знаю, что дизайн можно исправить, чтобы лучше использовать инъекции зависимостей. Можете ли вы показать мне на основе этого фрагмента, какие улучшения можно сделать, чтобы тесты работали?

Я боролся с этим и был бы очень признателен за помощь.

PS: Мне пришлось использовать setSqlMapClientTemplate(), потому что я хочу, чтобы вызов моего DAO был простым MyDAO myd = new MyDAO() Я не хочу создавать интерфейс для каждого из моих DAO.


person Drake    schedule 10.11.2009    source источник
comment
Почему, черт возьми, в ваш абстрактный класс встроено имя JNDI? Просто введите его с помощью Spring.   -  person duffymo    schedule 10.11.2009
comment
Это похоже на приложение, написанное для Spring кем-то, кто на самом деле не понимал Spring. Мне тебя жаль.   -  person matt b    schedule 10.11.2009
comment
Мне жаль себя ... за то, что я недостаточно хорошо знаю Spring, чтобы сам исправить этот код. вина моя ...   -  person Drake    schedule 10.11.2009


Ответы (1)


Здесь много проблем.

Во-первых, я считаю три цитаты из строки поиска JNDI в вашем небольшом примере. DRY посоветует вам написать его один раз и по возможности сослаться на него.

Во-вторых, я не очень ценю вашу DAO. Это действительно то, что вы пишете, или это просто пример? Я не думаю, что это идиома Spring. Интерфейса нет. Как вы будете проводить декларативные транзакции без него? Я бы рекомендовал посмотреть документы Spring для iBatis внимательнее.

В-третьих, я бы рекомендовал использовать JUnit 4.4 или, еще лучше, идиому TestNG - аннотации. Также ознакомьтесь с Spring @ContextConfiguration, чтобы внедрить bean-компоненты, которые вам нужны, в setUp.

В-четвертых, ваши DAO не могут работать, потому что вам нужна работающая служба поиска JNDI, и вы не можете получить ее без контейнера. Ответ - иметь источник данных DriverManager для ваших тестов.

ОБНОВЛЕНИЕ: вот идея, которую стоит попробовать: используйте Весенняя идиома для iBatis. Если наследие мешает вам сделать это, возможно, Spring не ваш ответ.

После этого все, что вам нужно сделать, это переопределить контекст приложения источника данных, чтобы использовать DriverManager вместо JNDI для вашего теста.

person duffymo    schedule 10.11.2009
comment
Во-первых: понимаете, что это плохо, но в каком месте я должен его хранить? Вы сказали, что инъекция использует пружину. Что именно это означает? Из трех ссылок я где-нибудь использую весеннюю инъекцию? как бы я это сделал? Второй: я меняю устаревший код. есть много кода, который вызывает методы DAO. Если я точно буду следовать идиоме Spring, мне придется вернуться и изменить способ вызова DAO. Четвертое: вместо MyServiceLocator я могу использовать источник данных DriverManager, но как насчет .getBean (MapClient) ;? как я получу это без контейнера? и большое спасибо - person Drake; 10.11.2009
comment
Я меняю устаревший код - тогда, возможно, Spring не подходит для вашей ситуации. Какую проблему ты пытаешься решить? - person duffymo; 10.11.2009
comment
В устаревшем коде я конвертирую код JDBC в iBatis. Если я извлечу имя JNDI из своего абстрактного класса, я не буду использовать MyServiceLocator. Но тогда как мне получить DataSource в классе MyAbstract? Я прочитал главу 11 Spring, но они не показывают, как получить имя JNDI как bean-компонент и получить от него источник данных .. - person Drake; 10.11.2009
comment
вы предоставляете сеттер - setDataSource (DataSource) - и разрешаете вводить его в контекст (файл xml) - person matt b; 10.11.2009
comment
все дело в том, что ваш DAO не привязан к определенному источнику данных или строке JNDI, что позволяет / легко предоставлять различные значения, фиктивные объекты и т. д. для тестирования. Это основная тема Spring - чтобы зависимости были внедрены в класс, а не класс, выходящий и находящий то, что ему нужно. - person matt b; 10.11.2009
comment
@matt: ваш комментарий помог мне найти эту ссылку: roseindia.net/spring/springpart3.shtml похоже, что я должен сделать. Хорошо, так что это приведет к удалению жестко закодированной строки JNDI в классе MyAbstract. Еще одна проблема заключается в том, что из тестового примера (вне контейнера) я могу получить bean-компонент MapClient (2-я строка блока try в getSqlTempl ()) - person Drake; 10.11.2009
comment
О нет, не смотрите на Rose India. Не лучший источник для чего-либо. - person duffymo; 11.11.2009