我一直试图用现有的Spring @TransactionalEvents
测试(通过@TransactionalTestExecutionListener
或子类AbstractTransactionalUnit4SpringContextTests
运行)来测试JUnit (Spring4.2的一个特性),但是,似乎有一个强制的选择--要么运行没有@Rollback
注释的测试,要么不触发事件。有没有人遇到过测试@TransactionalEvents的好方法,同时能够@Rollback测试?
发布于 2016-04-11 17:49:53
Stéphane Nicoll是正确的:如果您的@TransactionalEventListener
的@TransactionalEventListener
设置为AFTER_COMMIT
,那么使用自动回滚语义的事务性测试就没有任何意义,因为事件永远不会被触发。
换句话说,如果事务从未提交,则无法在事务提交后触发事件。
因此,如果您确实希望激发该事件,则必须让事务被提交(例如,使用@Commit
注释您的测试方法)。若要在提交之后进行清理,您应该能够在事务提交后使用隔离模式下的@Sql
执行清理脚本。例如,以下内容(未经测试的代码)可能适用于您:
@Transactional
@Commit
@Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = @SqlConfig(transactionMode = TransactionMode.ISOLATED))
@Test
public void test() { /* ... */ }
致以敬意,
Sam ( Spring TestContext框架的作者)
发布于 2020-01-23 09:03:53
Marco的解决方案可以工作,但是将REQUIRES_NEW传播添加到业务代码中并不总是可以接受的。这会修改业务流程行为。
因此,我们应该假设我们只能更改测试部分。
Solutin 1
@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
@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脚本,否则我们将使用提交的修改数据引入副作用。
发布于 2017-07-07 07:23:26
Sam的解决方案几乎适用于亚当的评论。
实际上,使用@TransactionalEventListener
注释的方法是在提交测试方法事务后调用的。这是因为引发事件的调用方法是在逻辑事务中执行的,而不是物理事务。
相反,当调用方法在新的物理事务中执行时,就会在正确的时间(即在提交测试方法事务之前)调用带有@TransactionalEventListener
注释的方法。
而且,我们不需要在测试方法上使用@Commit
,因为我们实际上并不关心这些事务。但是,正如Sam所解释的那样,我们确实需要@Sql(...)
语句来撤消调用方法的提交更改。
请参阅下面的小示例。
首先,提交事务时调用的侦听器(@TransactionalEventListener
的默认行为):
@Component
public class MyListener {
@TransactionalEventListener
public void when(MyEvent event) {
...
}
}
然后,发布事件的应用程序服务由上面的类听取。注意,每次调用方法时,事务都被配置为新的物理事务(有关更多细节,请参见Spring Framework doc ):
@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class MyApplicationService {
public void doSomething() {
// ...
// publishes an instance of MyEvent
// ...
}
}
最后,Sam提出的测试方法,但没有@Commit
注释,此时不需要该注释:
@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
// ...
}
这样它就像一种魅力:-)
https://stackoverflow.com/questions/36536466
复制相似问题