Nino
10/14/2021, 11:01 AMdelay
the emission of values in a flow, during a unit test...
The following code fails :
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.setMain
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
class CatRepository(
private val coroutineDispatcher: CoroutineDispatcher
) {
fun getMeowsFlow() = flow {
emit("Meow #1")
delay(3_000)
emit("Meow #2")
}.flowOn(coroutineDispatcher)
}
class CatRepositoryTest {
@get:Rule
val testCoroutineRule = TestCoroutineRule()
private val catRepository = CatRepository(testCoroutineRule.testCoroutineDispatcher)
@Test
fun catShouldMeowOnce() = testCoroutineRule.runBlockingTest {
pauseDispatcher()
assertEquals(
listOf("Meow #1"),
catRepository.getMeowsFlow().toList()
)
}
}
class TestCoroutineRule : TestRule {
val testCoroutineDispatcher = TestCoroutineDispatcher()
private val testCoroutineScope = TestCoroutineScope(testCoroutineDispatcher)
override fun apply(base: Statement, description: Description?) = object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
Dispatchers.setMain(testCoroutineDispatcher)
base.evaluate()
Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
testCoroutineScope.cleanupTestCoroutines()
}
}
fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
testCoroutineScope.runBlockingTest { block() }
}
It prints
expected:<[Meow #1]> but was:<[Meow #1, Meow #2]>
Expected :[Meow #1]
Actual :[Meow #1, Meow #2]
It makes no sense at all ! Since the virtual time is at 0ms, how come the delay
is completely ignored ? Could someone explain ?Joffrey
10/14/2021, 11:04 AMkotlinx-coroutines-test
, but in any case toList()
would hang forever if the flow
didn't progressNino
10/14/2021, 11:04 AMJoffrey
10/14/2021, 11:05 AMNino
10/14/2021, 11:06 AM@Test
fun catShouldMeow2After3000ms() = testCoroutineRule.runBlockingTest {
advanceTimeBy(3_001)
assertEquals(
"Meow #2",
catRepository.getMeowsFlow().first()
)
}
This test fails too, that's so disappointing 😞Joffrey
10/14/2021, 11:08 AMfirst()
here should return the first element, no matter how long you wait before calling it.ephemient
10/14/2021, 8:10 PMmyanmarking
10/15/2021, 9:40 AMlaunch{flow.toList} delay(x)