Séb*_*mer 5 java junit spring transactions mariadb
我有一个 Spring 服务可以执行类似的操作:
@Service
public class MyService {
@Transactional(propagation = Propagation.NEVER)
public void doStuff(UUID id) {
// call an external service, via http for example, can be long
// update the database, with a transactionTemplate for example
}
}
Run Code Online (Sandbox Code Playgroud)
Propagation.NEVER 指示调用该方法时我们不能有活动事务,因为我们不想在等待外部服务的答复时阻止与数据库的连接。
现在,我如何正确测试它然后回滚数据库?@Transactional 在测试中不起作用,因为 Propagation.NEVER 会出现异常。
@SpringBootTest
@Transactional
public class MyServiceTest {
@Autowired
private MyService myService;
public void testDoStuff() {
putMyTestDataInDb();
myService.doStuff(); // <- fails no transaction should be active
assertThat(myData).isTheWayIExpectedItToBe();
}
}
Run Code Online (Sandbox Code Playgroud)
我可以删除@Transactional,但是我的数据库对于下一个测试来说并没有处于一致的状态。
目前,我的解决方案是在 @AfterEach junit 回调中的每次测试后截断数据库的所有表,但这有点笨拙,并且当数据库有多个表时会变得相当慢。
我的问题是:如何在不截断/使用 @Transactional 的情况下回滚对数据库所做的更改?
我正在测试的数据库是带有 testcontainers 的 mariadb,因此仅适用于 mariadb/mysql 的解决方案对我来说就足够了。但更通用的东西会很棒!
(另一个例子,我希望能够在测试中不使用@Transactional:有时我想测试事务边界是否正确放入代码中,并且在运行时不会遇到一些延迟加载异常,因为我在某处忘记了@Transactional在生产代码中)。
其他一些精度,如果有帮助的话:
我玩过的其他想法:
START TRANSACTION在测试之前和ROLLBACK测试之后在数据源上本地发出语句:真的很脏,无法使其工作个人观点:@Transactional+ @SpringBootTest(在某种程度上)与 相同的反模式spring.jpa.open-in-view。是的,一开始很容易让事情正常运行,并且自动回滚很好,但它会失去很多灵活性和对事务的控制。任何需要手动事务管理的事情都很难以这种方式进行测试。
我们最近遇到了一个非常相似的案例,最后我们决定硬着头皮使用@DirtiesContext。是的,测试还需要 30 分钟才能运行,但作为一个额外的好处,测试服务的行为方式与生产中的完全相同,并且测试更有可能捕获任何事务问题。
但在进行切换之前,我们考虑使用以下解决方法:
interface TransactionService
{
void runWithoutTransaction(Runnable runnable);
}
Run Code Online (Sandbox Code Playgroud)
@Service
public class RealTransactionService implements TransactionService
{
@Transactional(propagation = Propagation.NEVER)
public void runWithoutTransaction(Runnable runnable)
{
runnable.run();
}
}
Run Code Online (Sandbox Code Playgroud)
#runWithoutTransaction,例如:@Service
public class MyService
{
@Autowired
private TransactionService transactionService;
public void doStuff(UUID id)
{
transactionService.runWithoutTransaction(() -> {
// call an external service
})
}
}
Run Code Online (Sandbox Code Playgroud)
这样您的生产代码将执行检查Propagation.NEVER,并且对于测试,您可以将其替换TransactionService为没有注释的不同实现@Transactional,例如:
@Service
@Primary
public class FakeTransactionService implements TransactionService
{
// No annotation here
public void runWithoutTransaction(Runnable runnable)
{
runnable.run();
}
}
Run Code Online (Sandbox Code Playgroud)
这不限于Propagation.NEVER。其他传播类型可以用同样的方式实现:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void runWithNewTransaction(Runnable runnable)
{
runnable.run();
}
Run Code Online (Sandbox Code Playgroud)
最后 -如果方法需要返回和/或接受一个值,则可以用//Runnable替换参数。FunctionConsumerSupplier
| 归档时间: |
|
| 查看次数: |
529 次 |
| 最近记录: |