我有一个小的Spring应用程序,它读取一些数据库,并在一个数据库中写一个小表。我有一个@Service-annotated类,带有@Transactional方法。该方法调用DAO类中的方法(不是@Repository-annotated),该方法首先从表中删除某些行,然后将行插入到同一个表中。
这将部署到WebLogic。在正常操作下,这个应用程序工作正常。
我尝试了一个故意将SQL插入“插入”的实验,并将其部署到我的本地框中,然后执行执行此服务操作的JMX操作。失败后(预期)我检查了数据库,并确认该表是完整的,所以当“插入”失败时它正确地回滚“删除”。
我的问题是我尝试模拟类似场景的集成测试不是在事务上表现。我模拟了JdbcTemplate,因此它执行了删除操作,但强制它在插入时抛出DataAccessException。之后,我检查了数据库,行已经消失了,所以它没有像我希望的那样回滚删除。
我打开了Spring JTA包中的debug,我看到调试消息打印出来说它正在回滚事务。
我正在使用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 :(得分:1)
问题在于模拟设置。在调用mock的update方法时,不会在测试方法中调用getOrigTemplate的更新。
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);
}
});
答案 1 :(得分:-1)
我使用注释来使测试类具有事务性。您可以参考http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/testing.html#testing-tx了解更多信息。