如何在集成测试中使用 Propagation.REQUIRES_NEW 回滚嵌套事务

Posted

技术标签:

【中文标题】如何在集成测试中使用 Propagation.REQUIRES_NEW 回滚嵌套事务【英文标题】:How to rollback nested transactions with Propagation.REQUIRES_NEW in integration tests 【发布时间】:2011-07-07 21:56:59 【问题描述】:

我有几个针对扩展以下基类的各种服务的集成测试:

@ContextConfiguration(locations="classpath:applicationContext-test.xml")
@TransactionConfiguration(transactionManager="txManager", defaultRollback=true)
@Transactional
public abstract class IntegrationTestBase extends AbstractTransactionalJUnit4SpringContextTests

    //Some setup, filling test data to a HSQLDB-database etc

在大多数情况下,这可以正常工作,但我有一个服务类,其中包含使用propagation=Propagation.REQUIRES_NEW 定义的事务。似乎这些事务没有回滚(因为它们是嵌套事务并且显然是在“外部”事务中提交的?)。至少根据测试日志,“外部”(测试用例级别)事务被回滚。提交的事务搞砸了一些后来的测试,因为它们改变了测试数据。

我可以通过强制测试在测试之间重新创建和重新填充数据库来解决这个问题,但我的问题是,这是预期的行为还是我在测试中做错了什么?嵌套事务能否从测试代码强制回滚?

【问题讨论】:

jira.springsource.org/browse/SPR-6908 上有一张改进票 【参考方案1】:

这是预期行为,也是使用 REQUIRES_NEW 的主要原因之一:

能够回滚新事务,但提交外部事务 能够提交新事务,但回滚外部事务

在测试之间重新填充数据库可能是最好的解决方案,我会为所有测试使用这个解决方案:这允许测试检查一切是否正常,包括提交(可能由于刷新、延迟约束而失败等)。

但如果你真的想回滚事务,一个解决方案是在你的服务中添加一个布尔参数rollbackAtTheEnd,如果这个参数为真,则回滚事务。

【讨论】:

谢谢,我想我会在测试之间重新填充数据库,即使测试以这种方式运行的时间更长。我没想到提交可能会失败,所以这也是提交测试的专家。有问题的服务有点特殊,因为它在 Spring Security 登录期间使用,并且在某些情况下,服务方法会抛出 JPA 异常,导致登录失败,因为整个事务被标记为回滚(这是为什么我选择使用 REQUIRES_NEW,所以异常不会阻止登录,另一个选项可能是在服务中使用 noRollbackFor。 您好,我遇到了同样的问题,但我不知道如何重新填充数据库或这有什么影响?你能告诉我这件事吗?谢谢 我希望有更好的解决方案,因为每次集成测试都需要花费大量时间来重新创建数据库。【参考方案2】:

我为此向 Spring improvement ticket 添加了评论。我这里也复制一下:

我通过转换像这样声明式设置的所有服务方法来解决这个问题

@Transactional(propagation = REQUIRES_NEW)
public Object doSmth() 
  // doSmthThatRequiresNewTx

改用TransactionTemplate

private TransactionTemplate transactionTemplate;

public Object doSmth() 
  return transactionTemplate.execute(new TransactionCallback<Object>() 
            @Override
            public Object doInTransaction(TransactionStatus status) 
                // doSmthThatRequiresNewTx
            
        );
  

在测试中我将transactionTemplate的传播行为配置为PROPAGATION_REQUIRED,在真实应用下我将transactionTemplate的传播行为配置为PROPAGATION_REQUIRES_NEW。它按预期工作。此解决方法的局限性在于,在测试中,无法断言内部事务在异常情况下回滚。

另一种解决方案是在测试中的@AfterTransaction 方法中显式删除doSmth() 在数据库中所做的一切。该“删除”SQL 将在新事务中运行,否则其结果将被 Spring 的 TransactionConfiguration 默认行为例行回滚。

【讨论】:

以上是关于如何在集成测试中使用 Propagation.REQUIRES_NEW 回滚嵌套事务的主要内容,如果未能解决你的问题,请参考以下文章

如何在集成测试中模拟 BLoC

如何在集成测试中等待某些操作

如何在集成测试中重置 Firestore 模拟器

如何在 Spring Boot 集成测试中使用嵌入式 MongoDB

使用Mybatis和不同DB进行集成测试开发时如何做集成测试

如何在集成测试中使用 Propagation.REQUIRES_NEW 回滚嵌套事务