runTest

fun runTest(context: CoroutineContext = EmptyCoroutineContext, timeout: Duration = DEFAULT_TIMEOUT.getOrThrow(), testBody: suspend TestScope.() -> Unit): TestResult(source)

Executes testBody as a test in a new coroutine, returning TestResult.

On JVM and Native, this function behaves similarly to runBlocking, with the difference that the code that it runs will skip delays. This allows to use delay in tests without causing them to take more time than necessary. On JS, this function creates a Promise that executes the test body with the delay-skipping behavior.

@Test
fun exampleTest() = runTest {
val deferred = async {
delay(1.seconds)
async {
delay(1.seconds)
}.await()
}

deferred.await() // result available immediately
}

The platform difference entails that, in order to use this function correctly in common code, one must always immediately return the produced TestResult from the test method, without doing anything else afterwards. See TestResult for details on this.

The test is run on a single thread, unless other CoroutineDispatcher are used for child coroutines. Because of this, child coroutines are not executed in parallel to the test body. In order for the spawned-off asynchronous code to actually be executed, one must either yield or suspend the test body some other way, or use commands that control scheduling (see TestCoroutineScheduler).

@Test
fun exampleWaitingForAsyncTasks1() = runTest {
// 1
val job = launch {
// 3
}
// 2
job.join() // the main test coroutine suspends here, so the child is executed
// 4
}

@Test
fun exampleWaitingForAsyncTasks2() = runTest {
// 1
launch {
// 3
}
// 2
testScheduler.advanceUntilIdle() // runs the tasks until their queue is empty
// 4
}

Task scheduling

Delay skipping is achieved by using virtual time. If Dispatchers.Main is set to a TestDispatcher via Dispatchers.setMain before the test, then its TestCoroutineScheduler is used; otherwise, a new one is automatically created (or taken from context in some way) and can be used to control the virtual time, advancing it, running the tasks scheduled at a specific time etc. The scheduler can be accessed via TestScope.testScheduler.

Delays in code that runs inside dispatchers that don't use a TestCoroutineScheduler don't get skipped:

@Test
fun exampleTest() = runTest {
val elapsed = TimeSource.Monotonic.measureTime {
val deferred = async {
delay(1.seconds) // will be skipped
withContext(Dispatchers.Default) {
delay(5.seconds) // Dispatchers.Default doesn't know about TestCoroutineScheduler
}
}
deferred.await()
}
println(elapsed) // about five seconds
}

Failures

Test body failures

If the created coroutine completes with an exception, then this exception will be thrown at the end of the test.

Timing out

There's a built-in timeout of 60 seconds for the test body. If the test body doesn't complete within this time, then the test fails with an AssertionError. The timeout can be changed for each test separately by setting the timeout parameter.

Additionally, setting the kotlinx.coroutines.test.default_timeout system property on the JVM to any string that can be parsed using Duration.parse (like 1m, 30s or 1500ms) will change the default timeout to that value for all tests whose timeout is not set explicitly; setting it to anything else will throw an exception every time runTest is invoked.

On timeout, the test body is cancelled so that the test finishes. If the code inside the test body does not respond to cancellation, the timeout will not be able to make the test execution stop. In that case, the test will hang despite the attempt to terminate it.

On the JVM, if DebugProbes from the kotlinx-coroutines-debug module are installed, the current dump of the coroutines' stack is printed to the console on timeout before the test body is cancelled.

Reported exceptions

Unhandled exceptions will be thrown at the end of the test. If uncaught exceptions happen after the test finishes, they are propagated in a platform-specific manner: see handleCoroutineException for details. If the test coroutine completes with an exception, the unhandled exceptions are suppressed by it.

Uncompleted coroutines

Otherwise, the test will hang until all the coroutines launched inside testBody complete. This may be an issue when there are some coroutines that are not supposed to complete, like infinite loops that perform some background work and are supposed to outlive the test. In that case, TestScope.backgroundScope can be used to launch such coroutines. They will be cancelled automatically when the test finishes.

Configuration

context can be used to affect the environment of the code under test. Beside just being passed to the coroutine scope created for the test, context also can be used to change how the test is executed. See the TestScope constructor function documentation for details.

Throws

if the context is invalid. See the TestScope constructor docs for details.


fun TestScope.runTest(timeout: Duration = DEFAULT_TIMEOUT.getOrThrow(), testBody: suspend TestScope.() -> Unit): TestResult(source)

Performs runTest on an existing TestScope. See the documentation for runTest for details.


fun runTest(context: CoroutineContext = EmptyCoroutineContext, dispatchTimeoutMs: Long, testBody: suspend TestScope.() -> Unit): TestResult(source)

Deprecated

Define a total timeout for the whole test instead of using dispatchTimeoutMs. Warning: the proposed replacement is not identical as it uses 'dispatchTimeoutMs' as the timeout for the whole test!

Replace with

import kotlin.time.Duration.Companion.milliseconds
runTest(context, timeout = dispatchTimeoutMs.milliseconds, testBody)

Executes testBody as a test in a new coroutine, returning TestResult.

On JVM and Native, this function behaves similarly to runBlocking, with the difference that the code that it runs will skip delays. This allows to use delay in without causing the tests to take more time than necessary. On JS, this function creates a Promise that executes the test body with the delay-skipping behavior.

@Test
fun exampleTest() = runTest {
val deferred = async {
delay(1.seconds)
async {
delay(1.seconds)
}.await()
}

deferred.await() // result available immediately
}

The platform difference entails that, in order to use this function correctly in common code, one must always immediately return the produced TestResult from the test method, without doing anything else afterwards. See TestResult for details on this.

The test is run in a single thread, unless other CoroutineDispatcher are used for child coroutines. Because of this, child coroutines are not executed in parallel to the test body. In order for the spawned-off asynchronous code to actually be executed, one must either yield or suspend the test body some other way, or use commands that control scheduling (see TestCoroutineScheduler).

@Test
fun exampleWaitingForAsyncTasks1() = runTest {
// 1
val job = launch {
// 3
}
// 2
job.join() // the main test coroutine suspends here, so the child is executed
// 4
}

@Test
fun exampleWaitingForAsyncTasks2() = runTest {
// 1
launch {
// 3
}
// 2
advanceUntilIdle() // runs the tasks until their queue is empty
// 4
}

Task scheduling

Delay-skipping is achieved by using virtual time. If Dispatchers.Main is set to a TestDispatcher via Dispatchers.setMain before the test, then its TestCoroutineScheduler is used; otherwise, a new one is automatically created (or taken from context in some way) and can be used to control the virtual time, advancing it, running the tasks scheduled at a specific time etc. Some convenience methods are available on TestScope to control the scheduler.

Delays in code that runs inside dispatchers that don't use a TestCoroutineScheduler don't get skipped:

@Test
fun exampleTest() = runTest {
val elapsed = TimeSource.Monotonic.measureTime {
val deferred = async {
delay(1.seconds) // will be skipped
withContext(Dispatchers.Default) {
delay(5.seconds) // Dispatchers.Default doesn't know about TestCoroutineScheduler
}
}
deferred.await()
}
println(elapsed) // about five seconds
}

Failures

Test body failures

If the created coroutine completes with an exception, then this exception will be thrown at the end of the test.

Reported exceptions

Unhandled exceptions will be thrown at the end of the test. If the uncaught exceptions happen after the test finishes, the error is propagated in a platform-specific manner. If the test coroutine completes with an exception, the unhandled exceptions are suppressed by it.

Uncompleted coroutines

This method requires that, after the test coroutine has completed, all the other coroutines launched inside testBody also complete, or are cancelled. Otherwise, the test will be failed (which, on JVM and Native, means that runTest itself will throw AssertionError, whereas on JS, the Promise will fail with it).

In the general case, if there are active jobs, it's impossible to detect if they are going to complete eventually due to the asynchronous nature of coroutines. In order to prevent tests hanging in this scenario, runTest will wait for dispatchTimeoutMs from the moment when TestCoroutineScheduler becomes idle before throwing AssertionError. If some dispatcher linked to TestCoroutineScheduler receives a task during that time, the timer gets reset.

Configuration

context can be used to affect the environment of the code under test. Beside just being passed to the coroutine scope created for the test, context also can be used to change how the test is executed. See the TestScope constructor function documentation for details.

Throws

if the context is invalid. See the TestScope constructor docs for details.


fun TestScope.runTest(dispatchTimeoutMs: Long, testBody: suspend TestScope.() -> Unit): TestResult(source)

Deprecated

Define a total timeout for the whole test instead of using dispatchTimeoutMs. Warning: the proposed replacement is not identical as it uses 'dispatchTimeoutMs' as the timeout for the whole test!

Replace with

import kotlin.time.Duration.Companion.milliseconds
this.runTest(timeout = dispatchTimeoutMs.milliseconds, testBody)

Performs runTest on an existing TestScope.

In the general case, if there are active jobs, it's impossible to detect if they are going to complete eventually due to the asynchronous nature of coroutines. In order to prevent tests hanging in this scenario, runTest will wait for dispatchTimeoutMs from the moment when TestCoroutineScheduler becomes idle before throwing AssertionError. If some dispatcher linked to TestCoroutineScheduler receives a task during that time, the timer gets reset.

fun TestCoroutineScope.runTest(dispatchTimeoutMs: Long = DEFAULT_DISPATCH_TIMEOUT_MS, block: suspend TestCoroutineScope.() -> Unit): TestResult(source)

Deprecated

Use `TestScope.runTest` instead.

Runs a test in a TestCoroutineScope based on this one.

Calls runTest using a coroutine context from this TestCoroutineScope. The TestCoroutineScope used to run the block will be different from this one, but will use its Job as a parent.

Since this function returns TestResult, in order to work correctly on the JS, its result must be returned immediately from the test body. See the docs for TestResult for details.