0

我有一个小型 Spring 应用程序,它读取几个数据库,并在一个数据库中写入一个小表。我有一个带有@Transactional 方法的@Service 注释类。该方法调用 DAO 类(不是 @Repository 注释的)中的方法,该方法首先从表中删除一些行,然后将行插入到同一个表中。

这将部署到 WebLogic。在正常操作下,此应用程序运行良好。

我尝试了一个故意为“insert”添加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 数据源”。

我可能会错过什么?

仅仅是因为免费的 Atomikos 事务管理器不是真正的事务性的吗?我可能会错过什么?

4

2 回答 2

1

问题在于模拟设置。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);
     }
 });
于 2011-12-10T05:29:29.473 回答
-1

我使用注释使测试类具有事务性。您可能想参考http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/testing.html#testing-tx了解更多信息。

于 2011-12-10T13:27:13.377 回答