【问题标题】:How do I get my Spring transactionmanager in my integration test to really be transactional?如何让我的集成测试中的 Spring 事务管理器真正具有事务性?
【发布时间】:2011-12-09 21:14:01
【问题描述】:

我有一个小型 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 事务管理器不是真正的事务性的吗? 我可能会错过什么?

【问题讨论】:

  • 你是如何获得 myServe 的 - 它是 AutoWired 吗?

标签: spring transactions atomikos


【解决方案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);
     }
 });

【讨论】:

  • 嗯,我非常希望这是对的。没错,但这显然不是唯一的问题。在实现了这个改变之后,我在调试器中单步调试了代​​码,并验证了“delete”调用只在“Answer”块中进行,我查看了堆栈,发现 TransactionInterceptor 在那里,所以这似乎是从事务中执行,但是一旦执行“更新”,我检查了数据库并且行已经消失了。我让测试完成,他们仍然走了。我认为事务管理器仍然是一个问题。
  • 你能检查一下dataSource的配置吗——你需要使用一个包装好的dataSource。您是否也为两者使用相同的数据库类型。您是否还可以使用 DataSourceTransactionManager 进行验证 - 这将有助于确定问题是否与 Atomikos 配置有关。现在有两个变量在起作用 - 你需要简化 - 让它工作并一次添加一个东西。
  • 在我的测试环境中,我使用“org.springframework.jdbc.datasource.DriverManagerDataSource”作为数据源。我不确定“包装的数据源”是什么意思。 test 和 appserver 的数据库类型相同,实际上它们是相同的数据库和表。测试上下文使用“org.springframework.transaction.jta.JtaTransactionManager”,就像在 appserver 上下文中一样。您是否建议我更改测试上下文以使用“org.springframework.jdbc.datasource.DataSourceTransactionManager”?如果是这样,我会尝试。
  • 是的 - 你可以尝试使用 DataSourceTransactionManager 进行测试 - 如果可行,那么测试和 spring 事务配置是正确的。然后我们可以对 Atomikos 配置进行故障排除。顺便说一句,您为什么需要 Atomikos - 您需要 XA / JTA 支持吗?
  • 我有 7 个应用程序正在使用的不同数据源。看起来 DataSourceTransactionManager 仅适用于单个数据源。关于我使用 Atomikos 的原因,我不知道有任何其他选项可用于具有多个数据源的独立集成测试(即使我的两阶段提交不需要跨多个数据源)。
【解决方案2】:

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

【讨论】:

  • 谢谢,但这不是这里的问题。
猜你喜欢
  • 2022-01-24
  • 1970-01-01
  • 2010-11-01
  • 1970-01-01
  • 2017-02-05
  • 2013-09-26
  • 1970-01-01
  • 1970-01-01
  • 2017-08-11
相关资源
最近更新 更多