If I want to test a function that launches coroutine that has a delay, what are the best practices for testing that function? Is it to use advanceUntilIdle()
, advanceTimeBy()
, something else, or some combination depending on the situation?
Here an example setup, which seems to be standard for org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.x
:
@ExperimentalCoroutinesApi
class MyViewModelTest {
@get:Rule
val mainDispatcherRule = TestMainDispatcherRule()
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
private lateinit var myViewModel: MyViewModel
@BeforeTest
fun setup() {
myViewModel = MyViewModel(mainDispatcherRule.dispatcher)
}
@Test
fun `test coroutine`() = runTest {
myViewModel.launchCoroutineWithDelay()
advanceUntilIdle()
// or advanceTimeBy(MyViewModel.DELAY_LENGTH)? Or something else?
assertTrue(myViewModel.coroutineFinished.value ?: false)
}
}
With a ViewModel like
@HiltViewModel
class MyViewModel @Inject constructor(
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
) {
val coroutineFinished = MutableLiveData(false)
fun launchCoroutineWithDelay() {
viewModelScope.launch(ioDispatcher) {
delay(DELAY_LENGTH)
coroutineFinished.postValue(true)
}
}
companion object {
const val DELAY_LENGTH = 300
}
}
And a TestMainDispatcherRule like
@ExperimentalCoroutinesApi
class TestMainDispatcherRule(val dispatcher: TestDispatcher = StandardTestDispatcher()) : TestWatcher() {
override fun starting(description: Description?) = Dispatchers.setMain(dispatcher)
override fun finished(description: Description?) = Dispatchers.resetMain()
}