在 Spring 测试测试方法上禁用事务

问题描述 投票:0回答:3

我将 TestNG 与 Spring 和 JPA 一起使用。到目前为止,我正在使用我的测试类来测试数据库内容,它扩展了

AbstractTransactionalTestNGSpringContextTests
。有了
@TransactionConfiguration(defaultRollback = true)
一切工作正常,我不需要关心清理。 Spring 在我的每个测试方法的开头创建一个默认事务,然后回滚。这是解决著名的“事务测试被认为有害”问题的一个非常巧妙的技巧。

不幸的是,我需要此类中的一种方法(一个测试)不具有此默认事务。这是因为此测试方法模拟批处理,并且其中有多个与生产无关的事务。我能够模拟和解决问题的唯一方法是使用

Propagation.REQUIRES_NEW
配置这些内部事务,但我不想在生产代码中使用它。

是否有某种方法可以为我的特定测试方法禁用 Spring 事务(这样我不需要在我的服务方法中使用

Propagation.REQUIRES_NEW
,而是使用
Propagation.REQUIRED
)?

java spring jpa transactions testng
3个回答
6
投票

我知道你已经解决了你的问题,但是对于将来来到这里的人们......

不幸的是,似乎没有办法禁用使用

@Transactional
注释的测试内的现有事务。

恕我直言,这里的 Spring 方法非常不灵活。但有一个解决方法可以解决您的问题。将所需的逻辑封装在 Spring

TransactionTemplate
类中就足够了。这将确保测试用例中的代码将在新事务中启动。


个人建议:从我的角度来看,最好和最灵活的方法是从一开始就放弃

@Transactional
测试并在每次测试之前将数据库设置为已知状态。这样,Spring 将以与生产中完全相同的方式管理事务。 没有怪癖,没有黑客,没有手动交易管理。

我知道使用

@Transactional

 以及围绕单元测试的“回滚”策略是一个诱人的想法,但它有太多陷阱。我建议阅读这篇文章
Spring 陷阱:事务测试被认为是有害的

当然我在这里并不是抱怨@Transactional本身——因为它极大地简化了生产代码中的事务管理。


3
投票
Spring Boot >=2.1.5.RELEASE 有以下解决方案。

默认情况下,数据 JPA 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅 Spring 框架参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个班级禁用事务管理,如下所示:

import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @DataJpaTest @Transactional(propagation = Propagation.NOT_SUPPORTED) public class ExampleNonTransactionalTests { }
(s。

Spring 文档


2
投票
我发现通过在单独的线程中执行测试主体可以防止 Spring 进行事务处理。所以解决方法是这样的:

@ContextConfiguration(classes = { test.SpringTestConfigurator.class }) @TransactionConfiguration(defaultRollback = false) @Slf4j @WebAppConfiguration public class DBDataTest extends AbstractTransactionalTestNGSpringContextTests { /** * Variable to determine if some running thread has failed. */ private volatile Exception threadException = null; @Test(enabled = true) public void myTest() { try { this.threadException = null; Runnable task = () -> { myTestBody(); }; ExecutorService executor = Executors.newFixedThreadPool(1); executor.submit(task); executor.shutdown(); while (!executor.isTerminated()) { if (this.threadException != null) { throw this.threadException; } } if (this.threadException != null) { throw this.threadException; } } catch (Exception e) { log.error("Test has failed.", e); Assert.fail(); } } public void myTestBody() { try { // test body to do } catch (Exception e) { this.threadException = e; } } }
    
© www.soinside.com 2019 - 2024. All rights reserved.