首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >测试@TransactionalEvents和@Rollback

测试@TransactionalEvents和@Rollback
EN

Stack Overflow用户
提问于 2016-04-10 22:50:01
回答 3查看 5K关注 0票数 5

我一直试图用现有的Spring @TransactionalEvents测试(通过@TransactionalTestExecutionListener或子类AbstractTransactionalUnit4SpringContextTests运行)来测试JUnit (Spring4.2的一个特性),但是,似乎有一个强制的选择--要么运行没有@Rollback注释的测试,要么不触发事件。有没有人遇到过测试@TransactionalEvents的好方法,同时能够@Rollback测试?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-04-11 17:49:53

Stéphane Nicoll是正确的:如果您的@TransactionalEventListener@TransactionalEventListener设置为AFTER_COMMIT,那么使用自动回滚语义的事务性测试就没有任何意义,因为事件永远不会被触发。

换句话说,如果事务从未提交,则无法在事务提交后触发事件。

因此,如果您确实希望激发该事件,则必须让事务被提交(例如,使用@Commit注释您的测试方法)。若要在提交之后进行清理,您应该能够在事务提交后使用隔离模式下的@Sql执行清理脚本。例如,以下内容(未经测试的代码)可能适用于您:

代码语言:javascript
运行
复制
@Transactional
@Commit
@Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
     config = @SqlConfig(transactionMode = TransactionMode.ISOLATED))
@Test
public void test() { /* ... */ }

致以敬意,

Sam ( Spring TestContext框架的作者)

票数 7
EN

Stack Overflow用户

发布于 2020-01-23 09:03:53

Marco的解决方案可以工作,但是将REQUIRES_NEW传播添加到业务代码中并不总是可以接受的。这会修改业务流程行为。

因此,我们应该假设我们只能更改测试部分。

Solutin 1

代码语言:javascript
运行
复制
@TestComponent // can be used with spring boot
public class TestApplicationService {

    @Autowired
    public MyApplicationService service;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void doSomething() {
        service.doSomething();
   }
}

这会将真正的服务封装到可以使用REQUIRES_NEW传播来修饰的测试组件中。此解决方案不修改测试以外的其他逻辑。

解决方案2

代码语言:javascript
运行
复制
@Transactional
@Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
     config = @SqlConfig(transactionMode = TransactionMode.ISOLATED))
@Test
public void test() { 
    MyApplicationService target = // ...
    target.doSomething();
    TestTransaction.flagForCommit(); //Spring-test since 4.1 - thx for Sam Brannen
    TestTransaction.end();
    // the event is now received by MyListener
    // assertions on the side effects of MyListener
    // ...
}

这是最简单的解决办法。我们可以结束测试事务并将其标记为提交。这将强制处理事务事件。如果测试更改数据,则必须指定清理sql脚本,否则我们将使用提交的修改数据引入副作用。

票数 4
EN

Stack Overflow用户

发布于 2017-07-07 07:23:26

Sam的解决方案几乎适用于亚当的评论。

实际上,使用@TransactionalEventListener注释的方法是在提交测试方法事务后调用的。这是因为引发事件的调用方法是在逻辑事务中执行的,而不是物理事务。

相反,当调用方法在新的物理事务中执行时,就会在正确的时间(即在提交测试方法事务之前)调用带有@TransactionalEventListener注释的方法。

而且,我们不需要在测试方法上使用@Commit,因为我们实际上并不关心这些事务。但是,正如Sam所解释的那样,我们确实需要@Sql(...)语句来撤消调用方法的提交更改。

请参阅下面的小示例。

首先,提交事务时调用的侦听器(@TransactionalEventListener的默认行为):

代码语言:javascript
运行
复制
@Component
public class MyListener {

    @TransactionalEventListener
    public void when(MyEvent event) {
        ...
    }
}

然后,发布事件的应用程序服务由上面的类听取。注意,每次调用方法时,事务都被配置为新的物理事务(有关更多细节,请参见Spring Framework doc ):

代码语言:javascript
运行
复制
@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class MyApplicationService {

    public void doSomething() {
        // ...
        // publishes an instance of MyEvent
        // ...
    }
}

最后,Sam提出的测试方法,但没有@Commit注释,此时不需要该注释:

代码语言:javascript
运行
复制
@Transactional
@Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
     config = @SqlConfig(transactionMode = TransactionMode.ISOLATED))
@Test
public void test() { 
    MyApplicationService target = // ...
    target.doSomething();
    // the event is now received by MyListener
    // assertions on the side effects of MyListener
    // ...
}

这样它就像一种魅力:-)

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36536466

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档