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:

@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()
}