azabost
05/13/2022, 7:48 PMfun asyncTest(
context: CoroutineContext = EmptyCoroutineContext, // in practice, it was never changed
timeoutInMillis: Long = 15000,
block: suspend CoroutineScope.() -> Unit
) {
runBlocking(context) {
withTimeout(timeoutInMillis, block)
}
}
It was supposed to fail the test if it took unexpectedly long to execute (real time, not virtual time) to avoid blocking the build agent in the CI system.
Now, after the migration to coroutines 1.6.x, I wanted to additionally use the new runTest
function that additionally checks if there are no active coroutines when the test is finished. Therefore I’m wondering if something like this is a good enough replacement for my previous utility:
fun asyncTest(
context: CoroutineContext = UnconfinedTestDispatcher(),
timeoutInMillis: Long = 15000,
block: suspend TestScope.() -> Unit
) = runBlocking {
withTimeout(timeoutInMillis) {
runTest(context, timeoutInMillis, block)
}
}
// EDIT: Nope, it doesn’t work the way I expected. It doesn’t work in the same way as my previous utility, e.g. when there is a coroutine with an endless while-loop with a delay inside it.
Any ideas?julian
05/13/2022, 7:55 PMrunTest
directly, instead of wrapping in runBlocking
and withTimeout
? I think runTest
gives you those behaviors already. Otherwise, the only reason for keeping asyncTest
would be the non-standard default arguments you provide.azabost
05/13/2022, 7:59 PMIn 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,I had a case when I was accidentally looping forever in a coroutine and the `runTest`’s timeout didn’t fail that test.will wait forrunTest
milliseconds (by default, 60 seconds) from the moment whendispatchTimeoutMs
becomes idle before throwingTestCoroutineScheduler
. If some dispatcher linked toAssertionError
receives a task during that time, the timer gets reset.TestCoroutineScheduler
azabost
05/13/2022, 8:11 PM@Test(expected = TimeoutCancellationException::class)
fun `check that running coroutine fails the test`() = asyncTest(timeoutInMillis = 5000L) {
val job = launch {
while (true) {
delay(1000)
}
}
job.join()
}
but when I use the new one (with runTest
and UnconfinedTestDisptatcher
), it hangs forever.julian
05/13/2022, 8:18 PMjulian
05/13/2022, 8:22 PMdispatchTimeoutMs
behaves. It's not a substitute for withTimeout
, after all.azabost
05/13/2022, 8:23 PMjulian
05/13/2022, 8:31 PM...runTest will wait for dispatchTimeoutMs milliseconds (by default, 60 seconds) from the moment when TestCoroutineScheduler becomes idle before throwing AssertionError.Then my next question was - So, how does the dispatcher become idle? Looks like changing dispatcher will do that. May not help you, but thought I'd share... Here
dispatchTimeoutMs
behaves like withTimeout
. On the downside, no time-savings from using the TestCoroutineScheduler
.
runTest(dispatchTimeoutMs = 1000) {
launch(<http://Dispatchers.IO|Dispatchers.IO>) {
while (true) {
delay(5000)
}
}
}
azabost
05/13/2022, 8:40 PM