CoroutineTestExtension
JUnit 5 ParameterResolverExtension for injecting and managing a TestProvidedCoroutineScope in a test instance. This creates a new instance of TestProvidedCoroutineScope each time the scope is injected, optionally using a custom ScopeFactory.
If this extension is initialized via RegisterExtension, there is also a scope property which is automatically managed.
Before Each:
Dispatchers.Main is set to the TestCoroutineDispatcher used by the CoroutineContext.
After Each:
cleanupTestCoroutines is called to ensure there are no leaking coroutines. Any unfinished coroutine will throw an UncompletedCoroutinesError.
Dispatchers.Main is reset via Dispatchers.resetMain.
Requires JUnit 5.
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.6.2")
}
Samples
import dispatch.test.TestProvidedCoroutineScope
import dispatch.test.coroutineTestExtension
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
fun main() {
//sampleStart
class RegisterSample {
@JvmField
@RegisterExtension
val extension = coroutineTestExtension()
@Test
fun `extension should be a TestProvidedCoroutineScope`() = runBlocking<Unit> {
extension.scope.shouldBeInstanceOf<TestProvidedCoroutineScope>()
}
@Test
fun `extension should automatically inject into functions`(scope: TestProvidedCoroutineScope) =
runBlocking {
val subject = SomeClass(scope)
val resultDeferred = subject.someFunction()
scope.advanceUntilIdle()
resultDeferred.await() shouldBe someValue
}
}
//sampleEnd
}
import dispatch.test.TestProvidedCoroutineScope
import dispatch.test.coroutineTestExtension
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
fun main() {
//sampleStart
class RegisterWithFactorySample {
@JvmField
@RegisterExtension
val extension = coroutineTestExtension {
TestProvidedCoroutineScope(context = CoroutineName("custom name"))
}
@Test
fun `extension should provide a scope from the custom factory`() = runBlocking {
extension.scope.coroutineContext[CoroutineName] shouldBe CoroutineName("custom name")
}
}
//sampleEnd
}
import dispatch.test.CoroutineTestExtension
import dispatch.test.TestProvidedCoroutineScope
import io.kotest.matchers.shouldNotBe
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
fun main() {
//sampleStart
@ExtendWith(CoroutineTestExtension::class)
class CoroutineTestExtensionExtendWithSample(
val testScope: TestProvidedCoroutineScope
) {
@Test
fun `injected scope should be injected`() {
testScope shouldNotBe null
}
}
//sampleEnd
}
See also
Parameters
optional factory for a custom TestProvidedCoroutineScope. If a factory is not provided, the resultant scope uses the same TestCoroutineDispatcher for each property in its TestDispatcherProvider
Constructors
Types
Class used to create the TestProvidedCoroutineScope used in CoroutineTestExtension.