Leverage JUnit 6 suspending function support

Closes gh-36215
This commit is contained in:
Sébastien Deleuze
2026-01-27 08:45:04 +01:00
parent 77c24b28b6
commit bc01eeb433
23 changed files with 287 additions and 539 deletions
@@ -42,14 +42,12 @@ class CoroutinesAnnotationTransactionInterceptorTests {
private val source = AnnotationTransactionAttributeSource()
@Test
fun suspendingNoValueSuccess() {
suspend fun suspendingNoValueSuccess() {
val proxyFactory = ProxyFactory()
proxyFactory.setTarget(TestWithCoroutines())
proxyFactory.addAdvice(TransactionInterceptor(rtm, source))
val proxy = proxyFactory.proxy as TestWithCoroutines
runBlocking {
proxy.suspendingNoValueSuccess()
}
proxy.suspendingNoValueSuccess()
assertReactiveGetTransactionAndCommitCount(1)
}
@@ -68,14 +66,12 @@ class CoroutinesAnnotationTransactionInterceptorTests {
}
@Test
fun suspendingValueSuccess() {
suspend fun suspendingValueSuccess() {
val proxyFactory = ProxyFactory()
proxyFactory.setTarget(TestWithCoroutines())
proxyFactory.addAdvice(TransactionInterceptor(rtm, source))
val proxy = proxyFactory.proxy as TestWithCoroutines
runBlocking {
assertThat(proxy.suspendingValueSuccess()).isEqualTo("foo")
}
assertThat(proxy.suspendingValueSuccess()).isEqualTo("foo")
assertReactiveGetTransactionAndCommitCount(1)
}
@@ -94,39 +90,33 @@ class CoroutinesAnnotationTransactionInterceptorTests {
}
@Test
fun suspendingFlowSuccess() {
suspend fun suspendingFlowSuccess() {
val proxyFactory = ProxyFactory()
proxyFactory.setTarget(TestWithCoroutines())
proxyFactory.addAdvice(TransactionInterceptor(rtm, source))
val proxy = proxyFactory.proxy as TestWithCoroutines
runBlocking {
assertThat(proxy.suspendingFlowSuccess().toList()).containsExactly("foo", "foo")
}
assertThat(proxy.suspendingFlowSuccess().toList()).containsExactly("foo", "foo")
assertReactiveGetTransactionAndCommitCount(1)
}
@Test
fun flowSuccess() {
suspend fun flowSuccess() {
val proxyFactory = ProxyFactory()
proxyFactory.setTarget(TestWithCoroutines())
proxyFactory.addAdvice(TransactionInterceptor(rtm, source))
val proxy = proxyFactory.proxy as TestWithCoroutines
runBlocking {
assertThat(proxy.flowSuccess().toList()).containsExactly("foo", "foo")
}
assertThat(proxy.flowSuccess().toList()).containsExactly("foo", "foo")
assertReactiveGetTransactionAndCommitCount(1)
}
@Test
fun suspendingValueSuccessWithContext() {
suspend fun suspendingValueSuccessWithContext() {
val proxyFactory = ProxyFactory()
proxyFactory.setTarget(TestWithCoroutines())
proxyFactory.addAdvice(TransactionInterceptor(rtm, source))
val proxy = proxyFactory.proxy as TestWithCoroutines
assertThat(runBlocking {
withExampleContext("context") {
proxy.suspendingValueSuccessWithContext()
}
assertThat(withExampleContext("context") {
proxy.suspendingValueSuccessWithContext()
}).isEqualTo("context")
assertReactiveGetTransactionAndCommitCount(1)
}
@@ -19,6 +19,7 @@ package org.springframework.transaction.interceptor
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.assertj.core.api.Fail
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -53,7 +54,7 @@ abstract class AbstractCoroutinesTransactionAspectTests {
}
@Test
fun noTransaction() {
suspend fun noTransaction() {
val rtm = Mockito.mock(ReactiveTransactionManager::class.java)
val tb = DefaultTestBean()
val tas: TransactionAttributeSource = MapTransactionAttributeSource()
@@ -63,9 +64,7 @@ abstract class AbstractCoroutinesTransactionAspectTests {
// and transaction attribute source
val itb = advised(tb, rtm, tas) as TestBean
checkReactiveTransaction(false)
runBlocking {
itb.getName()
}
itb.getName()
checkReactiveTransaction(false)
// expect no calls
@@ -76,7 +75,7 @@ abstract class AbstractCoroutinesTransactionAspectTests {
* Check that a transaction is created and committed.
*/
@Test
fun transactionShouldSucceed() {
suspend fun transactionShouldSucceed() {
val txatt: TransactionAttribute = DefaultTransactionAttribute()
val tas = MapTransactionAttributeSource()
tas.register(getNameMethod!!, txatt)
@@ -87,9 +86,7 @@ abstract class AbstractCoroutinesTransactionAspectTests {
given(rtm.commit(status)).willReturn(Mono.empty())
val tb = DefaultTestBean()
val itb = advised(tb, rtm, tas) as TestBean
runBlocking {
itb.getName()
}
itb.getName()
Mockito.verify(rtm).commit(status)
}
@@ -97,7 +94,7 @@ abstract class AbstractCoroutinesTransactionAspectTests {
* Check that two transactions are created and committed.
*/
@Test
fun twoTransactionsShouldSucceed() {
suspend fun twoTransactionsShouldSucceed() {
val txatt: TransactionAttribute = DefaultTransactionAttribute()
val tas1 = MapTransactionAttributeSource()
tas1.register(getNameMethod!!, txatt)
@@ -110,10 +107,8 @@ abstract class AbstractCoroutinesTransactionAspectTests {
given(rtm.commit(status)).willReturn(Mono.empty())
val tb = DefaultTestBean()
val itb = advised(tb, rtm, arrayOf(tas1, tas2)) as TestBean
runBlocking {
itb.getName()
itb.setName("myName")
}
itb.getName()
itb.setName("myName")
Mockito.verify(rtm, Mockito.times(2)).commit(status)
}
@@ -121,7 +116,7 @@ abstract class AbstractCoroutinesTransactionAspectTests {
* Check that a transaction is created and committed.
*/
@Test
fun transactionShouldSucceedWithNotNew() {
suspend fun transactionShouldSucceedWithNotNew() {
val txatt: TransactionAttribute = DefaultTransactionAttribute()
val tas = MapTransactionAttributeSource()
tas.register(getNameMethod!!, txatt)
@@ -132,9 +127,7 @@ abstract class AbstractCoroutinesTransactionAspectTests {
given(rtm.commit(status)).willReturn(Mono.empty())
val tb = DefaultTestBean()
val itb = advised(tb, rtm, tas) as TestBean
runBlocking {
itb.getName()
}
itb.getName()
Mockito.verify(rtm).commit(status)
}
@@ -254,14 +247,8 @@ abstract class AbstractCoroutinesTransactionAspectTests {
}
}
val itb = advised(tb, rtm, tas) as TestBean
runBlocking {
try {
itb.getName()
}
catch (actual: Exception) {
assertThat(actual).isInstanceOf(CannotCreateTransactionException::class.java)
}
}
assertThatExceptionOfType(CannotCreateTransactionException::class.java)
.isThrownBy { runBlocking { itb.getName() } }
}
/**
@@ -270,7 +257,7 @@ abstract class AbstractCoroutinesTransactionAspectTests {
* infrastructure exception was thrown to the client
*/
@Test
fun cannotCommitTransaction() {
suspend fun cannotCommitTransaction() {
val txatt: TransactionAttribute = DefaultTransactionAttribute()
val m = setNameMethod
val tas = MapTransactionAttributeSource()
@@ -286,17 +273,15 @@ abstract class AbstractCoroutinesTransactionAspectTests {
val tb = DefaultTestBean()
val itb = advised(tb, rtm, tas) as TestBean
val name = "new name"
runBlocking {
try {
itb.setName(name)
}
catch (actual: Exception) {
assertThat(actual).isInstanceOf(ex.javaClass)
assertThat(actual).hasMessage(ex.message)
}
// Should have invoked target and changed name
assertThat(itb.getName()).isEqualTo(name)
try {
itb.setName(name)
}
catch (actual: Exception) {
assertThat(actual).isInstanceOf(ex.javaClass)
assertThat(actual).hasMessage(ex.message)
}
// Should have invoked target and changed name
assertThat(itb.getName()).isEqualTo(name)
}
private fun checkReactiveTransaction(expected: Boolean) {
@@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatIllegalStateException
import org.junit.jupiter.api.Test
import org.springframework.transaction.support.DefaultTransactionDefinition
import kotlin.coroutines.AbstractCoroutineContextElement
@@ -33,13 +34,11 @@ class TransactionalOperatorExtensionsTests {
@Test
@Suppress("UNUSED_VARIABLE")
fun commitWithSuspendingFunction() {
suspend fun commitWithSuspendingFunction() {
val operator = TransactionalOperator.create(tm, DefaultTransactionDefinition())
runBlocking {
val returnValue: Boolean = operator.executeAndAwait {
delay(1)
true
}
val returnValue: Boolean = operator.executeAndAwait {
delay(1)
true
}
assertThat(tm.commit).isTrue()
assertThat(tm.rollback).isFalse()
@@ -47,13 +46,11 @@ class TransactionalOperatorExtensionsTests {
@Test
@Suppress("UNUSED_VARIABLE")
fun commitWithEmptySuspendingFunction() {
suspend fun commitWithEmptySuspendingFunction() {
val operator = TransactionalOperator.create(tm, DefaultTransactionDefinition())
runBlocking {
val returnValue: Boolean? = operator.executeAndAwait {
delay(1)
null
}
val returnValue: Boolean? = operator.executeAndAwait {
delay(1)
null
}
assertThat(tm.commit).isTrue()
assertThat(tm.rollback).isFalse()
@@ -62,22 +59,20 @@ class TransactionalOperatorExtensionsTests {
@Test
fun rollbackWithSuspendingFunction() {
val operator = TransactionalOperator.create(tm, DefaultTransactionDefinition())
runBlocking {
try {
assertThatIllegalStateException().isThrownBy {
runBlocking {
operator.executeAndAwait {
delay(1)
throw IllegalStateException()
}
} catch (ex: IllegalStateException) {
assertThat(tm.commit).isFalse()
assertThat(tm.rollback).isTrue()
return@runBlocking
}
}
assertThat(tm.commit).isFalse()
assertThat(tm.rollback).isTrue()
}
@Test
fun commitWithFlow() {
suspend fun commitWithFlow() {
val operator = TransactionalOperator.create(tm, DefaultTransactionDefinition())
val flow = flow {
emit(1)
@@ -85,10 +80,8 @@ class TransactionalOperatorExtensionsTests {
emit(3)
emit(4)
}
runBlocking {
val list = flow.transactional(operator).toList()
assertThat(list).hasSize(4)
}
val list = flow.transactional(operator).toList()
assertThat(list).hasSize(4)
assertThat(tm.commit).isTrue()
assertThat(tm.rollback).isFalse()
}
@@ -100,15 +93,13 @@ class TransactionalOperatorExtensionsTests {
delay(1)
throw IllegalStateException()
}
runBlocking {
try {
assertThatIllegalStateException().isThrownBy {
runBlocking {
flow.transactional(operator).toList()
} catch (ex: IllegalStateException) {
assertThat(tm.commit).isFalse()
assertThat(tm.rollback).isTrue()
return@runBlocking
}
}
assertThat(tm.commit).isFalse()
assertThat(tm.rollback).isTrue()
}
@Test