Best Practices Testing Coroutines With Delays?

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:

class MyViewModelTest {
    val mainDispatcherRule = TestMainDispatcherRule()

    val instantTaskExecutorRule = InstantTaskExecutorRule()

    private lateinit var myViewModel: MyViewModel

    fun setup() {
        myViewModel = MyViewModel(mainDispatcherRule.dispatcher)

    fun `test coroutine`() = runTest {
        // or advanceTimeBy(MyViewModel.DELAY_LENGTH)? Or something else?
        assertTrue(myViewModel.coroutineFinished.value ?: false)

With a ViewModel like

class MyViewModel @Inject constructor(
    @IoDispatcher private val ioDispatcher: CoroutineDispatcher
) {

    val coroutineFinished = MutableLiveData(false)

    fun launchCoroutineWithDelay() {
        viewModelScope.launch(ioDispatcher) {

    companion object {
        const val DELAY_LENGTH = 300

And a TestMainDispatcherRule like

class TestMainDispatcherRule(val dispatcher: TestDispatcher = StandardTestDispatcher()) : TestWatcher() {

    override fun starting(description: Description?) = Dispatchers.setMain(dispatcher)

    override fun finished(description: Description?) = Dispatchers.resetMain()