2011-12-09 2 views
0

У меня есть небольшое приложение Spring, которое читает несколько баз данных и пишет небольшую таблицу в одной базе данных. У меня есть класс @ Service-annotated с методом @Transactional. Этот метод вызывает метод в классе DAO (который не является @ Repository-annotated), который сначала удаляет некоторые строки из таблицы, а затем вставляет строки в одну и ту же таблицу.Как я могу заставить свой Spring transactionmanager в моем интеграционном тесте действительно быть транзакционным?

Это развертывается в WebLogic. При нормальной работе это приложение работает отлично.

Я попробовал эксперимент по умышленному удалению SQL для «вставки» и развернул его в своем локальном поле, а затем выполнил операцию JMX, которая выполняет эту служебную операцию. После того, как он потерпел неудачу (ожидается), я проверил базу данных, и я подтвердил, что таблица была неповрежденной, поэтому она корректно откатывала «удалить», когда «вставка» не удалась.

Моя проблема заключается в том, что мой интеграционный тест, который пытается имитировать подобный сценарий, НЕ ведет себя транзакционно. Я издевался над JdbcTemplate, поэтому он выполнил удаление, но заставил его бросить исключение DataAccessException. Впоследствии я проверил базу данных, и строки исчезли, поэтому она не отменила удаление, как я надеялся.

Я включил отладку в пакете Spring JTA, и я увидел отладочную распечатку сообщения, в котором говорится, что он откатывает транзакцию.

Я использую диспетчер транзакций Atomikos для своих тестов. Ниже приведен фрагмент из контекста, который я использую в тесте, чтобы определить «catalogTransactionManager», на что ссылается в остальной части контекста.

<!-- Construct Atomikos UserTransactionManager, needed to configure Spring --> 
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" 
     init-method="init" destroy-method="close"> 
    <!-- when close is called, should we force transactions to terminate or not? --> 
    <property name="forceShutdown"> 
     <value>true</value> 
    </property> 
</bean> 

<!-- Also use Atomikos UserTransactionImp, needed to configure Spring --> 
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> 
    <property name="transactionTimeout"> 
     <value>300</value> 
    </property> 
</bean> 

<!-- Configure the Spring framework to use JTA transactions from Atomikos --> 
<bean id="catalogTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 
    <property name="transactionManager"> 
     <ref bean="atomikosTransactionManager" /> 
    </property> 
    <property name="userTransaction"> 
     <ref bean="atomikosUserTransaction" /> 
    </property> 
</bean> 

Это, вероятно, не имеет значения, но вот мой метод испытания (с некоторыми вещами затемненный):

@Test 
public void testInsertFailsAfterDelete() { 
List<ErrorMessageInfo> commonErrorMessagesBefore = 
    myService. 
    getMyDAO().getCommonErrorMessages(MyService.CHANNEL_NAME); 

JdbcTemplate template = mock(JdbcTemplate.class); 
myService.getMyDAO().setJdbcTemplate(template); 

when(template.update(eq(MyDAO.SQL_DELETE_CHANNEL), any())). 
thenReturn(getOrigTemplate().update(MyDAO.SQL_DELETE_CHANNEL, MyService.CHANNEL_NAME)); 

DataAccessException exception = new DataAccessException("insert failed") {}; 

when(template.update(eq(MyDAO.SQL_INSERT_ERROR_TO_CHANNEL), anyString(), anyString(), anyInt(), any(), anyInt())). 
thenThrow(exception); 

try { 
myService.updateCommonErrorMessages(); 
fail(); 
} 
catch (Exception ex) { 
assertThat(ex).isEqualTo(exception); 
} 
finally { 
restoreTemplate(); 
} 

List<ErrorMessageInfo> commonErrorMessagesAfter = 
    myService. 
    getMyDAO().getCommonErrorMessages(MyService.CHANNEL_NAME); 

assertThat(commonErrorMessagesBefore).isEqualTo(commonErrorMessagesAfter); 

}

Обратите внимание, что, хотя, когда я раскрываю к WebLogic, я определяю обычный транзакционный источник данных объединения пула соединений, но в моем интеграционном тесте источник данных использует «org.springframework.jdbc.datasource.DriverManager DataSource».

Что я могу пропустить?

Это просто потому, что бесплатный менеджер транзакций Atomikos не является транзакционным? Что я могу пропустить?

+0

как вы получаете mySerive - это AutoWired? – gkamal

+0

Да, myService автоуведомлен. –

ответ

1

Проблема с макетной настройкой. Обновление на getOrigTemplate вызывается в методе тестирования не при вызове метода обновления для mock.

when(template.update(eq(MyDAO.SQL_DELETE_CHANNEL), any())). 
thenReturn(getOrigTemplate().update(MyDAO.SQL_DELETE_CHANNEL, MyService.CHANNEL_NAME)); 

Вы должны делать что-то подобное, чтобы получить нужное поведение.

when(template.update(eq(MyDAO.SQL_DELETE_CHANNEL), any())).thenAnswer(new Answer() { 
    Object answer(InvocationOnMock invocation) { 
     return getOrigTemplate().update(MyDAO.SQL_DELETE_CHANNEL,  
         MyService.CHANNEL_NAME); 
    } 
}); 
+0

Хм, я очень надеялся, что это было правильно. Это правильно, но это, по-видимому, не единственная проблема.После выполнения этого изменения я прошел через код в отладчике и подтвердил, что вызов «удалить» был сделан только в блоке «Ответ», и я посмотрел на стек и увидел, что TransactionInterceptor был там, поэтому это оказалось выполненный из транзакции, но как только он выполнил «обновление», я проверил базу данных, и строки исчезли. Я позволил пройти тест, и они все еще исчезли. Я думаю, что диспетчер транзакций по-прежнему остается проблемой. –

+0

Можете ли вы проверить конфигурацию dataSource - вам нужно использовать обернутый источник данных. Также вы используете тот же тип базы данных для обоих. Можете ли вы также проверить с помощью DataSourceTransactionManager - это поможет сгладить, если проблема связана с конфигурацией Atomikos. Сейчас в игре есть две переменные - вам нужно будет упростить - заставить ее работать и добавить одну вещь за раз. – gkamal

+0

В моем тестовом контенте я использую «org.springframework.jdbc.datasource.DriverManagerDataSource» для источников данных. Я не уверен, что вы подразумеваете под «обернутым источником данных». Тип базы данных одинаковый для test и appserver, на самом деле это одна и та же база данных и таблица для каждого. Контекст тестирования использует «org.springframework.transaction.jta.JtaTransactionManager», как и в контексте appserver. Вы предлагаете мне изменить тестовый контекст, чтобы вместо этого использовать «org.springframework.jdbc.datasource.DataSourceTransactionManager»? Если да, я попробую это. –

Смежные вопросы