CoroutineTestExtension

@ExperimentalCoroutinesApi
class CoroutineTestExtension(scopeFactory: CoroutineTestExtension.ScopeFactory) : TypeBasedParameterResolver<TestProvidedCoroutineScope> , BeforeEachCallback, AfterEachCallback

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

scopeFactory

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

Link copied to clipboard
fun CoroutineTestExtension(scopeFactory: CoroutineTestExtension.ScopeFactory = ScopeFactory())

Types

Link copied to clipboard
@ExperimentalCoroutinesApi
open class ScopeFactory

Class used to create the TestProvidedCoroutineScope used in CoroutineTestExtension.

Functions

Link copied to clipboard
override fun supportsParameter(p0: ParameterContext, p1: ExtensionContext): Boolean

Properties

Link copied to clipboard
val scope: TestProvidedCoroutineScope

A lazy TestProvidedCoroutineScope instance which is reset via cleanUpTestCoroutines after each test.

Sources

Link copied to clipboard