如何让我的集成测试中的Spring transactionman真正成为事务性的?

时间:2011-12-09 21:14:01

标签: spring transactions atomikos

我有一个小的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交易经理不是真正的交易吗? 我可能会失踪什么?

2 个答案:

答案 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了解更多信息。